6 Author  : Skrol29 (email: http://www.tinybutstrong.com/onlyyou.html)
 
   8 This class is independent from any other classes and has been originally created for the OpenTbs plug-in
 
   9 for TinyButStrong Template Engine (TBS). OpenTbs makes TBS able to merge OpenOffice and Ms Office documents.
 
  10 Visit http://www.tinybutstrong.com
 
  13 define('TBSZIP_DOWNLOAD',1);   // download (default)
 
  14 define('TBSZIP_NOHEADER',4);   // option to use with DOWNLOAD: no header is sent
 
  15 define('TBSZIP_FILE',8);       // output to file  , or add from file
 
  16 define('TBSZIP_STRING',32);    // output to string, or add from string
 
  20         function __construct() {
 
  21                 $this->Meth8Ok = extension_loaded('zlib'); // check if Zlib extension is available. This is need for compress and uncompress with method 8.
 
  22                 $this->DisplayError = true;
 
  27         function CreateNew($ArchName='new.zip') {
 
  28         // Create a new virtual empty archive, the name will be the default name when the archive is flushed.
 
  29                 if (!isset($this->Meth8Ok)) $this->__construct();  // for PHP 4 compatibility
 
  30                 $this->Close(); // note that $this->ArchHnd is set to false here
 
  32                 $this->ArchFile = $ArchName;
 
  33                 $this->ArchIsNew = true;
 
  34                 $bin = 'PK'.chr(05).chr(06).str_repeat(chr(0), 18);
 
  35                 $this->CdEndPos = strlen($bin) - 4;
 
  36                 $this->CdInfo = array('disk_num_curr'=>0, 'disk_num_cd'=>0, 'file_nbr_curr'=>0, 'file_nbr_tot'=>0, 'l_cd'=>0, 'p_cd'=>0, 'l_comm'=>0, 'v_comm'=>'', 'bin'=>$bin);
 
  37                 $this->CdPos = $this->CdInfo['p_cd'];
 
  40         function Open($ArchFile, $UseIncludePath=false) {
 
  41         // Open the zip archive
 
  42                 if (!isset($this->Meth8Ok)) $this->__construct();  // for PHP 4 compatibility
 
  43                 $this->Close(); // close handle and init info
 
  45                 $this->ArchFile = $ArchFile;
 
  46                 $this->ArchIsNew = false;
 
  48                 $this->ArchHnd = fopen($ArchFile, 'rb', $UseIncludePath);
 
  49                 $ok = !($this->ArchHnd===false);
 
  50                 if ($ok) $ok = $this->CentralDirRead();
 
  55                 if (isset($this->ArchHnd) and ($this->ArchHnd!==false)) fclose($this->ArchHnd);
 
  57                 $this->ArchHnd = false;
 
  58                 $this->CdInfo = array();
 
  59                 $this->CdFileLst = array();
 
  61                 $this->CdFileByName = array();
 
  62                 $this->VisFileLst = array();
 
  63                 $this->ArchCancelModif();
 
  66         function ArchCancelModif() {
 
  67                 $this->LastReadComp = false; // compression of the last read file (1=compressed, 0=stored not compressed, -1= stored compressed but read uncompressed)
 
  68                 $this->LastReadIdx = false;  // index of the last file read
 
  69                 $this->ReplInfo = array();
 
  70                 $this->ReplByPos = array();
 
  71                 $this->AddInfo = array();
 
  74         function FileAdd($Name, $Data, $DataType=TBSZIP_STRING, $Compress=true) {
 
  76                 if ($Data===false) return $this->FileCancelModif($Name, false); // Cancel a previously added file
 
  78                 // Save information for adding a new file into the archive
 
  79                 $Diff = 30 + 46 + 2*strlen($Name); // size of the header + cd info
 
  80                 $Ref = $this->_DataCreateNewRef($Data, $DataType, $Compress, $Diff, $Name);
 
  81                 if ($Ref===false) return false;
 
  83                 $this->AddInfo[] = $Ref;
 
  88         function CentralDirRead() {
 
  89                 $cd_info = 'PK'.chr(05).chr(06); // signature of the Central Directory
 
  91                 $this->_MoveTo($cd_pos, SEEK_END);
 
  92                 $b = $this->_ReadData(4);
 
  93                 if ($b!==$cd_info) return $this->RaiseError('The End of Central Rirectory Record is not found.');
 
  95                 $this->CdEndPos = ftell($this->ArchHnd) - 4;
 
  96                 $this->CdInfo = $this->CentralDirRead_End($cd_info);
 
  97                 $this->CdFileLst = array();
 
  98                 $this->CdFileNbr = $this->CdInfo['file_nbr_curr'];
 
  99                 $this->CdPos = $this->CdInfo['p_cd'];
 
 101                 if ($this->CdFileNbr<=0) return $this->RaiseError('No header found in the Central Directory.');
 
 102                 if ($this->CdPos<=0) return $this->RaiseError('No position found for the Central Directory.');
 
 104                 $this->_MoveTo($this->CdPos);
 
 105                 for ($i=0;$i<$this->CdFileNbr;$i++) {
 
 106                         $x = $this->CentralDirRead_File($i);
 
 108                                 $this->CdFileLst[$i] = $x;
 
 109                                 $this->CdFileByName[$x['v_name']] = $i;
 
 115         function CentralDirRead_End($cd_info) {
 
 116                 $b = $cd_info.$this->_ReadData(18);
 
 118                 $x['disk_num_curr'] = $this->_GetDec($b,4,2);  // number of this disk
 
 119                 $x['disk_num_cd'] = $this->_GetDec($b,6,2);    // number of the disk with the start of the central directory
 
 120                 $x['file_nbr_curr'] = $this->_GetDec($b,8,2);  // total number of entries in the central directory on this disk
 
 121                 $x['file_nbr_tot'] = $this->_GetDec($b,10,2);  // total number of entries in the central directory
 
 122                 $x['l_cd'] = $this->_GetDec($b,12,4);          // size of the central directory
 
 123                 $x['p_cd'] = $this->_GetDec($b,16,4);          // position of start of central directory with respect to the starting disk number
 
 124                 $x['l_comm'] = $this->_GetDec($b,20,2);        // .ZIP file comment length
 
 125                 $x['v_comm'] = $this->_ReadData($x['l_comm']); // .ZIP file comment
 
 126                 $x['bin'] = $b.$x['v_comm'];
 
 130         function CentralDirRead_File($idx) {
 
 132                 $b = $this->_ReadData(46);
 
 134                 $x = $this->_GetHex($b,0,4);
 
 135                 if ($x!=='h:02014b50') return $this->RaiseError("Signature of Central Directory Header #".$idx." (file information) expected but not found at position ".$this->_TxtPos(ftell($this->ArchHnd) - 46).".");
 
 138                 $x['vers_used'] = $this->_GetDec($b,4,2);
 
 139                 $x['vers_necess'] = $this->_GetDec($b,6,2);
 
 140                 $x['purp'] = $this->_GetBin($b,8,2);
 
 141                 $x['meth'] = $this->_GetDec($b,10,2);
 
 142                 $x['time'] = $this->_GetDec($b,12,2);
 
 143                 $x['date'] = $this->_GetDec($b,14,2);
 
 144                 $x['crc32'] = $this->_GetDec($b,16,4);
 
 145                 $x['l_data_c'] = $this->_GetDec($b,20,4);
 
 146                 $x['l_data_u'] = $this->_GetDec($b,24,4);
 
 147                 $x['l_name'] = $this->_GetDec($b,28,2);
 
 148                 $x['l_fields'] = $this->_GetDec($b,30,2);
 
 149                 $x['l_comm'] = $this->_GetDec($b,32,2);
 
 150                 $x['disk_num'] = $this->_GetDec($b,34,2);
 
 151                 $x['int_file_att'] = $this->_GetDec($b,36,2);
 
 152                 $x['ext_file_att'] = $this->_GetDec($b,38,4);
 
 153                 $x['p_loc'] = $this->_GetDec($b,42,4);
 
 154                 $x['v_name'] = $this->_ReadData($x['l_name']);
 
 155                 $x['v_fields'] = $this->_ReadData($x['l_fields']);
 
 156                 $x['v_comm'] = $this->_ReadData($x['l_comm']);
 
 158                 $x['bin'] = $b.$x['v_name'].$x['v_fields'].$x['v_comm'];
 
 163         function RaiseError($Msg) {
 
 164                 if ($this->DisplayError) echo '<strong>'.get_class($this).' ERROR :</strong> '.$Msg.'<br>'."\r\n";
 
 169         function Debug($FileHeaders=false) {
 
 171                 $this->DisplayError = true;
 
 174                         // Calculations first in order to have error messages before other information
 
 177                         $pos_stop = $this->CdInfo['p_cd'];
 
 178                         $this->_MoveTo($pos);
 
 179                         while ( ($pos<$pos_stop) && ($ok = $this->_ReadFile($idx,false)) ) {
 
 180                                 $this->VisFileLst[$idx]['p_this_header (debug_mode only)'] = $pos;
 
 181                                 $pos = ftell($this->ArchHnd);
 
 189                 echo "-------------------------------".$nl;
 
 190                 echo "End of Central Directory record".$nl;
 
 191                 echo "-------------------------------".$nl;
 
 192                 print_r($this->DebugArray($this->CdInfo));
 
 195                 echo "-------------------------".$nl;
 
 196                 echo "Central Directory headers".$nl;
 
 197                 echo "-------------------------".$nl;
 
 198                 print_r($this->DebugArray($this->CdFileLst));
 
 202                         echo "------------------".$nl;
 
 203                         echo "Local File headers".$nl;
 
 204                         echo "------------------".$nl;
 
 205                         print_r($this->DebugArray($this->VisFileLst));
 
 212         function DebugArray($arr) {
 
 213                 foreach ($arr as $k=>$v) {
 
 215                                 $arr[$k] = $this->DebugArray($v);
 
 216                         } elseif (substr($k,0,2)=='p_') {
 
 217                                 $arr[$k] = $this->_TxtPos($v);
 
 223         function FileExists($NameOrIdx) {
 
 224                 return ($this->FileGetIdx($NameOrIdx)!==false);
 
 227         function FileGetIdx($NameOrIdx) {
 
 228         // Check if a file name, or a file index exists in the Central Directory, and return its index
 
 229                 if (is_string($NameOrIdx)) {
 
 230                         if (isset($this->CdFileByName[$NameOrIdx])) {
 
 231                                 return $this->CdFileByName[$NameOrIdx];
 
 236                         if (isset($this->CdFileLst[$NameOrIdx])) {
 
 244         function FileGetIdxAdd($Name) {
 
 245         // Check if a file name exists in the list of file to add, and return its index
 
 246                 if (!is_string($Name)) return false;
 
 247                 $idx_lst = array_keys($this->AddInfo);
 
 248                 foreach ($idx_lst as $idx) {
 
 249                         if ($this->AddInfo[$idx]['name']===$Name) return $idx;
 
 254         function FileRead($NameOrIdx, $Uncompress=true) {
 
 256                 $this->LastReadComp = false; // means the file is not found
 
 257                 $this->LastReadIdx = false;
 
 259                 $idx = $this->FileGetIdx($NameOrIdx);
 
 260                 if ($idx===false) return $this->RaiseError('File "'.$NameOrIdx.'" is not found in the Central Directory.');
 
 262                 $pos = $this->CdFileLst[$idx]['p_loc'];
 
 263                 $this->_MoveTo($pos);
 
 265                 $this->LastReadIdx = $idx; // Can be usefull to get the idx
 
 267                 $Data = $this->_ReadFile($idx, true);
 
 269                 // Manage uncompression
 
 270                 $Comp = 1; // means the contents stays compressed
 
 271                 $meth = $this->CdFileLst[$idx]['meth'];
 
 274                                 if ($this->Meth8Ok) {
 
 275                                         $Data = gzinflate($Data);
 
 276                                         $Comp = -1; // means uncompressed
 
 278                                         $this->RaiseError('Unable to uncompress file "'.$NameOrIdx.'" because extension Zlib is not installed.');
 
 282                         $Comp = 0; // means stored without compression
 
 284                         if ($Uncompress) $this->RaiseError('Unable to uncompress file "'.$NameOrIdx.'" because it is compressed with method '.$meth.'.');
 
 286                 $this->LastReadComp = $Comp;
 
 292         function _ReadFile($idx, $ReadData) {
 
 293         // read the file header (and maybe the data ) in the archive, assuming the cursor in at a new file position
 
 295                 $b = $this->_ReadData(30);
 
 297                 $x = $this->_GetHex($b,0,4);
 
 298                 if ($x!=='h:04034b50') return $this->RaiseError("Signature of Local File Header #".$idx." (data section) expected but not found at position ".$this->_TxtPos(ftell($this->ArchHnd)-30).".");
 
 301                 $x['vers'] = $this->_GetDec($b,4,2);
 
 302                 $x['purp'] = $this->_GetBin($b,6,2);
 
 303                 $x['meth'] = $this->_GetDec($b,8,2);
 
 304                 $x['time'] = $this->_GetDec($b,10,2);
 
 305                 $x['date'] = $this->_GetDec($b,12,2);
 
 306                 $x['crc32'] = $this->_GetDec($b,14,4);
 
 307                 $x['l_data_c'] = $this->_GetDec($b,18,4);
 
 308                 $x['l_data_u'] = $this->_GetDec($b,22,4);
 
 309                 $x['l_name'] = $this->_GetDec($b,26,2);
 
 310                 $x['l_fields'] = $this->_GetDec($b,28,2);
 
 311                 $x['v_name'] = $this->_ReadData($x['l_name']);
 
 312                 $x['v_fields'] = $this->_ReadData($x['l_fields']);
 
 314                 $x['bin'] = $b.$x['v_name'].$x['v_fields'];
 
 317                 if (isset($this->CdFileLst[$idx])) {
 
 318                         $len_cd = $this->CdFileLst[$idx]['l_data_c'];
 
 319                         if ($x['l_data_c']==0) {
 
 320                                 // Sometimes, the size is not specified in the local information.
 
 323                                 $len = $x['l_data_c'];
 
 325                                         //echo "TbsZip Warning: Local information for file #".$idx." says len=".$len.", while Central Directory says len=".$len_cd.".";
 
 329                         $len = $x['l_data_c'];
 
 330                         if ($len==0) $this->RaiseError("File Data #".$idx." cannt be read because no length is specified in the Local File Header and its Central Directory information has not been found.");
 
 334                         $Data = $this->_ReadData($len);
 
 336                         $this->_MoveTo($len, SEEK_CUR);
 
 339                 // Description information
 
 340                 $desc_ok = ($x['purp'][2+3]=='1');
 
 342                         $b = $this->_ReadData(12);
 
 343                         $s = $this->_GetHex($b,0,4);
 
 345                         // the specification says the signature may or may not be present
 
 346                         if ($s=='h:08074b50') {
 
 347                                 $b .= $this->_ReadData(4); 
 
 350                                 $x['desc_sign'] = $s;
 
 354                         $x['desc_crc32']    = $this->_GetDec($b,0+$d,4);
 
 355                         $x['desc_l_data_c'] = $this->_GetDec($b,4+$d,4);
 
 356                         $x['desc_l_data_u'] = $this->_GetDec($b,8+$d,4);
 
 359                 // Save file info without the data
 
 360                 $this->VisFileLst[$idx] = $x;
 
 371         function FileReplace($NameOrIdx, $Data, $DataType=TBSZIP_STRING, $Compress=true) {
 
 372         // Store replacement information.
 
 374                 $idx = $this->FileGetIdx($NameOrIdx);
 
 375                 if ($idx===false) return $this->RaiseError('File "'.$NameOrIdx.'" is not found in the Central Directory.');
 
 377                 $pos = $this->CdFileLst[$idx]['p_loc'];
 
 381                         $this->ReplInfo[$idx] = false;
 
 385                         $Diff = - $this->CdFileLst[$idx]['l_data_c'];
 
 386                         $Ref = $this->_DataCreateNewRef($Data, $DataType, $Compress, $Diff, $NameOrIdx);
 
 387                         if ($Ref===false) return false;
 
 388                         $this->ReplInfo[$idx] = $Ref;
 
 389                         $Result = $Ref['res'];
 
 392                 $this->ReplByPos[$pos] = $idx;
 
 398         function FileCancelModif($NameOrIdx, $ReplacedAndDeleted=true) {
 
 399         // cancel added, modified or deleted modifications on a file in the archive
 
 400         // return the number of cancels
 
 404                 if ($ReplacedAndDeleted) {
 
 405                         // replaced or deleted files
 
 406                         $idx = $this->FileGetIdx($NameOrIdx);
 
 408                                 if (isset($this->ReplInfo[$idx])) {
 
 409                                         $pos = $this->CdFileLst[$idx]['p_loc'];
 
 410                                         unset($this->ReplByPos[$pos]);
 
 411                                         unset($this->ReplInfo[$idx]);
 
 418                 $idx = $this->FileGetIdxAdd($NameOrIdx);
 
 420                         unset($this->AddInfo[$idx]);
 
 428         function Flush($Render=TBSZIP_DOWNLOAD, $File='', $ContentType='') {
 
 430                 if ( ($File!=='') && ($this->ArchFile===$File)) {
 
 431                         $this->RaiseError('Method Flush() cannot overwrite the current opened archive: \''.$File.'\''); // this makes corrupted zip archives without PHP error.
 
 437                 $FicNewPos = array();
 
 438                 $DelLst = array(); // idx of deleted files
 
 439                 $DeltaCdLen = 0; // delta of the CD's size
 
 442                 $date  = $this->_MsDos_Date($now);
 
 443                 $time  = $this->_MsDos_Time($now);
 
 445                 if (!$this->OutputOpen($Render, $File, $ContentType)) return false;
 
 447                 // output modified zipped files and unmodified zipped files that are beetween them
 
 448                 ksort($this->ReplByPos);
 
 449                 foreach ($this->ReplByPos as $ReplPos => $ReplIdx) {
 
 450                         // output data from the zip archive which is before the data to replace
 
 451                         $this->OutputFromArch($ArchPos, $ReplPos);
 
 452                         // get current file information
 
 453                         if (!isset($this->VisFileLst[$ReplIdx])) $this->_ReadFile($ReplIdx, false);
 
 454                         $FileInfo =& $this->VisFileLst[$ReplIdx];
 
 455                         $b1 = $FileInfo['bin'];
 
 456                         if (isset($FileInfo['desc_bin'])) {
 
 457                                 $b2 = $FileInfo['desc_bin'];
 
 461                         $info_old_len = strlen($b1) + $this->CdFileLst[$ReplIdx]['l_data_c'] + strlen($b2); // $FileInfo['l_data_c'] may have a 0 value in some archives
 
 462                         // get replacement information
 
 463                         $ReplInfo =& $this->ReplInfo[$ReplIdx];
 
 464                         if ($ReplInfo===false) {
 
 465                                 // The file is to be deleted
 
 466                                 $Delta = $Delta - $info_old_len; // headers and footers are also deleted
 
 467                                 $DelLst[$ReplIdx] = true;
 
 469                                 // prepare the header of the current file
 
 470                                 $this->_DataPrepare($ReplInfo); // get data from external file if necessary
 
 471                                 $this->_PutDec($b1, $time, 10, 2); // time
 
 472                                 $this->_PutDec($b1, $date, 12, 2); // date
 
 473                                 $this->_PutDec($b1, $ReplInfo['crc32'], 14, 4); // crc32
 
 474                                 $this->_PutDec($b1, $ReplInfo['len_c'], 18, 4); // l_data_c
 
 475                                 $this->_PutDec($b1, $ReplInfo['len_u'], 22, 4); // l_data_u
 
 476                                 if ($ReplInfo['meth']!==false) $this->_PutDec($b1, $ReplInfo['meth'], 8, 2); // meth
 
 477                                 // prepare the bottom description if the zipped file, if any
 
 479                                         $d = (strlen($b2)==16) ? 4 : 0; // offset because of the signature if any
 
 480                                         $this->_PutDec($b2, $ReplInfo['crc32'], $d+0, 4); // crc32
 
 481                                         $this->_PutDec($b2, $ReplInfo['len_c'], $d+4, 4); // l_data_c
 
 482                                         $this->_PutDec($b2, $ReplInfo['len_u'], $d+8, 4); // l_data_u
 
 485                                 $this->OutputFromString($b1.$ReplInfo['data'].$b2);
 
 486                                 unset($ReplInfo['data']); // save PHP memory
 
 487                                 $Delta = $Delta + $ReplInfo['diff'] + $ReplInfo['len_c'];
 
 489                         // Update the delta of positions for zipped files which are physically after the currently replaced one
 
 490                         for ($i=0;$i<$this->CdFileNbr;$i++) {
 
 491                                 if ($this->CdFileLst[$i]['p_loc']>$ReplPos) {
 
 492                                         $FicNewPos[$i] = $this->CdFileLst[$i]['p_loc'] + $Delta;
 
 495                         // Update the current pos in the archive
 
 496                         $ArchPos = $ReplPos + $info_old_len;
 
 499                 // Ouput all the zipped files that remain before the Central Directory listing
 
 500                 if ($this->ArchHnd!==false) $this->OutputFromArch($ArchPos, $this->CdPos); // ArchHnd is false if CreateNew() has been called
 
 501                 $ArchPos = $this->CdPos;
 
 503                 // Output file to add
 
 504                 $AddNbr = count($this->AddInfo);
 
 505                 $AddDataLen = 0; // total len of added data (inlcuding file headers)
 
 507                         $AddPos = $ArchPos + $Delta; // position of the start
 
 508                         $AddLst = array_keys($this->AddInfo);
 
 509                         foreach ($AddLst as $idx) {
 
 510                                 $n = $this->_DataOuputAddedFile($idx, $AddPos);
 
 516                 // Modifiy file information in the Central Directory for replaced files
 
 519                 for ($i=0;$i<$this->CdFileNbr;$i++) {
 
 520                         $b1 = $this->CdFileLst[$i]['bin'];
 
 521                         $old_cd_len += strlen($b1);
 
 522                         if (!isset($DelLst[$i])) {
 
 523                                 if (isset($FicNewPos[$i])) $this->_PutDec($b1, $FicNewPos[$i], 42, 4);   // p_loc
 
 524                                 if (isset($this->ReplInfo[$i])) {
 
 525                                         $ReplInfo =& $this->ReplInfo[$i];
 
 526                                         $this->_PutDec($b1, $time, 12, 2); // time
 
 527                                         $this->_PutDec($b1, $date, 14, 2); // date
 
 528                                         $this->_PutDec($b1, $ReplInfo['crc32'], 16, 4); // crc32
 
 529                                         $this->_PutDec($b1, $ReplInfo['len_c'], 20, 4); // l_data_c
 
 530                                         $this->_PutDec($b1, $ReplInfo['len_u'], 24, 4); // l_data_u
 
 531                                         if ($ReplInfo['meth']!==false) $this->_PutDec($b1, $ReplInfo['meth'], 10, 2); // meth
 
 536                 $this->OutputFromString($b2);
 
 537                 $ArchPos += $old_cd_len;
 
 538                 $DeltaCdLen =  $DeltaCdLen + strlen($b2) - $old_cd_len;
 
 540                 // Output until "end of central directory record"
 
 541                 if ($this->ArchHnd!==false) $this->OutputFromArch($ArchPos, $this->CdEndPos); // ArchHnd is false if CreateNew() has been called
 
 543                 // Output file information of the Central Directory for added files
 
 546                         foreach ($AddLst as $idx) {
 
 547                                 $b2 .= $this->AddInfo[$idx]['bin'];
 
 549                         $this->OutputFromString($b2);
 
 550                         $DeltaCdLen += strlen($b2);
 
 553                 // Output "end of central directory record"
 
 554                 $b2 = $this->CdInfo['bin'];
 
 555                 $DelNbr = count($DelLst);
 
 556                 if ( ($AddNbr>0) or ($DelNbr>0) ) {
 
 557                         // total number of entries in the central directory on this disk
 
 558                         $n = $this->_GetDec($b2, 8, 2);
 
 559                         $this->_PutDec($b2, $n + $AddNbr - $DelNbr,  8, 2);
 
 560                         // total number of entries in the central directory
 
 561                         $n = $this->_GetDec($b2, 10, 2);
 
 562                         $this->_PutDec($b2, $n + $AddNbr - $DelNbr, 10, 2);
 
 563                         // size of the central directory
 
 564                         $n = $this->_GetDec($b2, 12, 4);
 
 565                         $this->_PutDec($b2, $n + $DeltaCdLen, 12, 4);
 
 566                         $Delta = $Delta + $AddDataLen;
 
 568                 $this->_PutDec($b2, $this->CdPos+$Delta , 16, 4); // p_cd  (offset of start of central directory with respect to the starting disk number)
 
 569                 $this->OutputFromString($b2);
 
 571                 $this->OutputClose();
 
 581         function OutputOpen($Render, $File, $ContentType) {
 
 583                 if (($Render & TBSZIP_FILE)==TBSZIP_FILE) {
 
 584                         $this->OutputMode = TBSZIP_FILE;
 
 585                         if (''.$File=='') $File = basename($this->ArchFile).'.zip';
 
 586                         $this->OutputHandle = @fopen($File, 'w');
 
 587                         if ($this->OutputHandle===false) {
 
 588                                 $this->RaiseError('Method Flush() cannot overwrite the target file \''.$File.'\'. This may not be a valid file path or the file may be locked by another process or because of a denied permission.');
 
 591                 } elseif (($Render & TBSZIP_STRING)==TBSZIP_STRING) {
 
 592                         $this->OutputMode = TBSZIP_STRING;
 
 593                         $this->OutputSrc = '';
 
 594                 } elseif (($Render & TBSZIP_DOWNLOAD)==TBSZIP_DOWNLOAD) {
 
 595                         $this->OutputMode = TBSZIP_DOWNLOAD;
 
 597                         if (''.$File=='') $File = basename($this->ArchFile);
 
 598                         if (($Render & TBSZIP_NOHEADER)==TBSZIP_NOHEADER) {
 
 600                                 header ('Pragma: no-cache');
 
 601                                 if ($ContentType!='') header ('Content-Type: '.$ContentType);
 
 602                                 header('Content-Disposition: attachment; filename="'.$File.'"');
 
 603                                 header('Expires: 0');
 
 604                                 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
 
 605                                 header('Cache-Control: public');
 
 606                                 header('Content-Description: File Transfer'); 
 
 607                                 header('Content-Transfer-Encoding: binary');
 
 608                                 $Len = $this->_EstimateNewArchSize();
 
 609                                 if ($Len!==false) header('Content-Length: '.$Len); 
 
 617         function OutputFromArch($pos, $pos_stop) {
 
 618                 $len = $pos_stop - $pos;
 
 620                 $this->_MoveTo($pos);
 
 623                         $l = min($len, $block);
 
 624                         $x = $this->_ReadData($l);
 
 625                         $this->OutputFromString($x);
 
 631         function OutputFromString($data) {
 
 632                 if ($this->OutputMode===TBSZIP_DOWNLOAD) {
 
 633                         echo $data; // donwload
 
 634                 } elseif ($this->OutputMode===TBSZIP_STRING) {
 
 635                         $this->OutputSrc .= $data; // to string
 
 636                 } elseif (TBSZIP_FILE) {
 
 637                         fwrite($this->OutputHandle, $data); // to file
 
 641         function OutputClose() {
 
 642                 if ( ($this->OutputMode===TBSZIP_FILE) && ($this->OutputHandle!==false) ) {
 
 643                         fclose($this->OutputHandle);
 
 644                         $this->OutputHandle = false;
 
 652         function _MoveTo($pos, $relative = SEEK_SET) {
 
 653                 fseek($this->ArchHnd, $pos, $relative);
 
 656         function _ReadData($len) {
 
 658                         $x = fread($this->ArchHnd, $len);
 
 666         // Take info from binary data
 
 669         function _GetDec($txt, $pos, $len) {
 
 670                 $x = substr($txt, $pos, $len);
 
 672                 for ($i=0;$i<$len;$i++) {
 
 674                         if ($asc>0) $z = $z + $asc*pow(256,$i);
 
 679         function _GetHex($txt, $pos, $len) {
 
 680                 $x = substr($txt, $pos, $len);
 
 681                 return 'h:'.bin2hex(strrev($x));
 
 684         function _GetBin($txt, $pos, $len) {
 
 685                 $x = substr($txt, $pos, $len);
 
 687                 for ($i=0;$i<$len;$i++) {
 
 690                                 for ($j=0;$j<8;$j++) {
 
 691                                         $z .= ($asc & pow(2,$j)) ? '1' : '0';
 
 701         // Put info into binary data
 
 704         function _PutDec(&$txt, $val, $pos, $len) {
 
 706                 for ($i=0;$i<$len;$i++) {
 
 710                                 $z = intval($val % 256);
 
 711                                 if (($val<0) && ($z!=0)) { // ($z!=0) is very important, example: val=-420085702
 
 712                                         // special opration for negative value. If the number id too big, PHP stores it into a signed integer. For example: crc32('coucou') => -256185401 instead of  4038781895. NegVal = BigVal - (MaxVal+1) = BigVal - 256^4
 
 713                                         $val = ($val - $z)/256 -1;
 
 716                                         $val = ($val - $z)/256;
 
 721                 $txt = substr_replace($txt, $x, $pos, $len);
 
 724         function _MsDos_Date($Timestamp = false) {
 
 725                 // convert a date-time timstamp into the MS-Dos format
 
 726                 $d = ($Timestamp===false) ? getdate() : getdate($Timestamp);
 
 727                 return (($d['year']-1980)*512) + ($d['mon']*32) + $d['mday'];
 
 729         function _MsDos_Time($Timestamp = false) {
 
 730                 // convert a date-time timstamp into the MS-Dos format
 
 731                 $d = ($Timestamp===false) ? getdate() : getdate($Timestamp);
 
 732                 return ($d['hours']*2048) + ($d['minutes']*32) + intval($d['seconds']/2); // seconds are rounded to an even number in order to save 1 bit
 
 735         function _MsDos_Debug($date, $time) {
 
 736                 // Display the formated date and time. Just for debug purpose.
 
 737                 // date end time are encoded on 16 bits (2 bytes) : date = yyyyyyymmmmddddd , time = hhhhhnnnnnssssss
 
 738                 $y = ($date & 65024)/512 + 1980;
 
 739                 $m = ($date & 480)/32;
 
 741                 $h = ($time & 63488)/2048;
 
 742                 $i = ($time & 1984)/32;
 
 743                 $s = ($time & 31) * 2; // seconds have been rounded to an even number in order to save 1 bit
 
 744                 return $y.'-'.str_pad($m,2,'0',STR_PAD_LEFT).'-'.str_pad($d,2,'0',STR_PAD_LEFT).' '.str_pad($h,2,'0',STR_PAD_LEFT).':'.str_pad($i,2,'0',STR_PAD_LEFT).':'.str_pad($s,2,'0',STR_PAD_LEFT);
 
 747         function _TxtPos($pos) {
 
 748                 // Return the human readable position in both decimal and hexa
 
 749                 return $pos." (h:".dechex($pos).")";
 
 752         function _DataOuputAddedFile($Idx, $PosLoc) {
 
 754                 $Ref =& $this->AddInfo[$Idx];
 
 755                 $this->_DataPrepare($Ref); // get data from external file if necessary
 
 759                 $date  = $this->_MsDos_Date($now);
 
 760                 $time  = $this->_MsDos_Time($now);
 
 761                 $len_n = strlen($Ref['name']);
 
 762                 $purp  = 2048 ; // purpose // +8 to indicates that there is an extended local header 
 
 764                 // Header for file in the data section 
 
 765                 $b = 'PK'.chr(03).chr(04).str_repeat(' ',26); // signature
 
 766                 $this->_PutDec($b,20,4,2); //vers = 20
 
 767                 $this->_PutDec($b,$purp,6,2); // purp
 
 768                 $this->_PutDec($b,$Ref['meth'],8,2);  // meth
 
 769                 $this->_PutDec($b,$time,10,2); // time
 
 770                 $this->_PutDec($b,$date,12,2); // date
 
 771                 $this->_PutDec($b,$Ref['crc32'],14,4); // crc32
 
 772                 $this->_PutDec($b,$Ref['len_c'],18,4); // l_data_c
 
 773                 $this->_PutDec($b,$Ref['len_u'],22,4); // l_data_u
 
 774                 $this->_PutDec($b,$len_n,26,2); // l_name
 
 775                 $this->_PutDec($b,0,28,2); // l_fields
 
 776                 $b .= $Ref['name']; // name
 
 780                 $this->OutputFromString($b.$Ref['data']);
 
 781                 $OutputLen = strlen($b) + $Ref['len_c']; // new position of the cursor
 
 782                 unset($Ref['data']); // save PHP memory
 
 784                 // Information for file in the Central Directory
 
 785                 $b = 'PK'.chr(01).chr(02).str_repeat(' ',42); // signature
 
 786                 $this->_PutDec($b,20,4,2);  // vers_used = 20
 
 787                 $this->_PutDec($b,20,6,2);  // vers_necess = 20
 
 788                 $this->_PutDec($b,$purp,8,2);  // purp
 
 789                 $this->_PutDec($b,$Ref['meth'],10,2); // meth
 
 790                 $this->_PutDec($b,$time,12,2); // time
 
 791                 $this->_PutDec($b,$date,14,2); // date
 
 792                 $this->_PutDec($b,$Ref['crc32'],16,4); // crc32
 
 793                 $this->_PutDec($b,$Ref['len_c'],20,4); // l_data_c
 
 794                 $this->_PutDec($b,$Ref['len_u'],24,4); // l_data_u
 
 795                 $this->_PutDec($b,$len_n,28,2); // l_name
 
 796                 $this->_PutDec($b,0,30,2); // l_fields
 
 797                 $this->_PutDec($b,0,32,2); // l_comm
 
 798                 $this->_PutDec($b,0,34,2); // disk_num
 
 799                 $this->_PutDec($b,0,36,2); // int_file_att
 
 800                 $this->_PutDec($b,0,38,4); // ext_file_att
 
 801                 $this->_PutDec($b,$PosLoc,42,4); // p_loc
 
 802                 $b .= $Ref['name']; // v_name
 
 803                 $b .= ''; // v_fields
 
 812         function _DataCreateNewRef($Data, $DataType, $Compress, $Diff, $NameOrIdx) {
 
 814                 if (is_array($Compress)) {
 
 816                         $meth = $Compress['meth'];
 
 817                         $len_u = $Compress['len_u'];
 
 818                         $crc32 = $Compress['crc32'];
 
 820                 } elseif ($Compress and ($this->Meth8Ok)) {
 
 823                         $len_u = false; // means unknown
 
 826                         $result = ($Compress) ? -1 : 0;
 
 833                 if ($DataType==TBSZIP_STRING) {
 
 836                                 // we compress now in order to save PHP memory
 
 837                                 $len_u = strlen($Data);
 
 838                                 $crc32 = crc32($Data);
 
 839                                 $Data = gzdeflate($Data);
 
 840                                 $len_c = strlen($Data);
 
 842                                 $len_c = strlen($Data);
 
 843                                 if ($len_u===false) {
 
 845                                         $crc32 = crc32($Data);
 
 851                         if (file_exists($path)) {
 
 852                                 $fz = filesize($path);
 
 853                                 if ($len_u===false) $len_u = $fz;
 
 854                                 $len_c = ($Compress) ? false : $fz;
 
 856                                 return $this->RaiseError("Cannot add the file '".$path."' because it is not found.");
 
 860                 // at this step $Data and $crc32 can be false only in case of external file, and $len_c is false only in case of external file to compress
 
 861                 return array('data'=>$Data, 'path'=>$path, 'meth'=>$meth, 'len_u'=>$len_u, 'len_c'=>$len_c, 'crc32'=>$crc32, 'diff'=>$Diff, 'res'=>$result);
 
 865         function _DataPrepare(&$Ref) {
 
 866         // returns the real size of data
 
 867                 if ($Ref['path']!==false) {
 
 868                         $Ref['data'] = file_get_contents($Ref['path']);
 
 869                         if ($Ref['crc32']===false) $Ref['crc32'] = crc32($Ref['data']);
 
 870                         if ($Ref['len_c']===false) {
 
 871                                 // means the data must be compressed
 
 872                                 $Ref['data'] = gzdeflate($Ref['data']);
 
 873                                 $Ref['len_c'] = strlen($Ref['data']);
 
 878         function _EstimateNewArchSize($Optim=true) {
 
 879         // Return the size of the new archive, or false if it cannot be calculated (because of external file that must be compressed before to be insered)
 
 881                 if ($this->ArchIsNew) {
 
 882                         $Len = strlen($this->CdInfo['bin']);
 
 884                         $Len = filesize($this->ArchFile);
 
 887                 // files to replace or delete
 
 888                 foreach ($this->ReplByPos as $i) {
 
 889                         $Ref =& $this->ReplInfo[$i];
 
 892                                 $Info =& $this->CdFileLst[$i];
 
 893                                 if (!isset($this->VisFileLst[$i])) {
 
 894                                         if ($Optim) return false; // if $Optimization is set to true, then we d'ont rewind to read information
 
 895                                         $this->_MoveTo($Info['p_loc']);
 
 896                                         $this->_ReadFile($i, false);
 
 898                                 $Vis =& $this->VisFileLst[$i];
 
 899                                 $Len += -strlen($Vis['bin']) -strlen($Info['bin']) - $Info['l_data_c'];
 
 900                                 if (isset($Vis['desc_bin'])) $Len += -strlen($Vis['desc_bin']);
 
 901                         } elseif ($Ref['len_c']===false) {
 
 902                                 return false; // information not yet known
 
 905                                 $Len += $Ref['len_c'] + $Ref['diff'];
 
 910                 $i_lst = array_keys($this->AddInfo);
 
 911                 foreach ($i_lst as $i) {
 
 912                         $Ref =& $this->AddInfo[$i];
 
 913                         if ($Ref['len_c']===false) {
 
 914                                 return false; // information not yet known
 
 916                                 $Len += $Ref['len_c'] + $Ref['diff'];