1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4: */ 3// +----------------------------------------------------------------------+ 4// | PHP Version 4 | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 1997-2002 The PHP Group | 7// +----------------------------------------------------------------------+ 8// | This source file is subject to version 2.02 of the PHP license, | 9// | that is bundled with this package in the file LICENSE, and is | 10// | available at through the world-wide-web at | 11// | http://www.php.net/license/2_02.txt. | 12// | If you did not receive a copy of the PHP license and are unable to | 13// | obtain it through the world-wide-web, please send a note to | 14// | license@php.net so we can mail you a copy immediately. | 15// +----------------------------------------------------------------------+ 16// | Author: Xavier Noguer <xnoguer@php.net> | 17// | Based on OLE::Storage_Lite by Kawai, Takanori | 18// +----------------------------------------------------------------------+ 19// 20// $Id$ 21 22 23/** 24* Class for creating Root PPS's for OLE containers 25* 26* @author Xavier Noguer <xnoguer@php.net> 27* @category Structures 28* @package OLE 29*/ 30class OLE_PPS_Root extends OLE_PPS 31{ 32 /** 33 * The temporary dir for storing the OLE file 34 * @var string 35 */ 36 var $_tmp_dir; 37 38 /** 39 * Constructor 40 * 41 * @access public 42 * @param integer $time_1st A timestamp 43 * @param integer $time_2nd A timestamp 44 */ 45 function construct($time_1st, $time_2nd, $raChild) 46 { 47 $this->_tmp_dir = ''; 48 $this->OLE_PPS( 49 null, 50 OLE::Asc2Ucs('Root Entry'), 51 OLE_PPS_TYPE_ROOT, 52 null, 53 null, 54 null, 55 $time_1st, 56 $time_2nd, 57 null, 58 $raChild); 59 } 60 61 /** 62 * Sets the temp dir used for storing the OLE file 63 * 64 * @access public 65 * @param string $dir The dir to be used as temp dir 66 * @return true if given dir is valid, false otherwise 67 */ 68 function setTempDir($dir) 69 { 70 if (is_dir($dir)) { 71 $this->_tmp_dir = $dir; 72 return true; 73 } 74 return false; 75 } 76 77 /** 78 * Method for saving the whole OLE container (including files). 79 * In fact, if called with an empty argument (or '-'), it saves to a 80 * temporary file and then outputs it's contents to stdout. 81 * 82 * @param string $filename The name of the file where to save the OLE container 83 * @access public 84 * @return mixed true on success, PEAR_Error on failure 85 */ 86 function save($filename) 87 { 88 // Initial Setting for saving 89 $this->_BIG_BLOCK_SIZE = pow(2, 90 ((isset($this->_BIG_BLOCK_SIZE))? $this->_adjust2($this->_BIG_BLOCK_SIZE) : 9)); 91 $this->_SMALL_BLOCK_SIZE= pow(2, 92 ((isset($this->_SMALL_BLOCK_SIZE))? $this->_adjust2($this->_SMALL_BLOCK_SIZE): 6)); 93 94 // Open temp file if we are sending output to stdout 95 if (($filename == '-') or ($filename == '')) 96 { 97 $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); 98 $this->_FILEH_ = @fopen($this->_tmp_filename,"w+b"); 99 if ($this->_FILEH_ == false) { 100 return $this->raiseError("Can't create temporary file."); 101 } 102 } 103 else 104 { 105 $this->_FILEH_ = @fopen($filename, "wb"); 106 if ($this->_FILEH_ == false) { 107 return $this->raiseError("Can't open $filename. It may be in use or protected."); 108 } 109 } 110 // Make an array of PPS's (for Save) 111 $aList = array(); 112 $this->_savePpsSetPnt($aList); 113 // calculate values for header 114 list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo); 115 // Save Header 116 $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); 117 118 // Make Small Data string (write SBD) 119 $this->_data = $this->_makeSmallData($aList); 120 121 // Write BB 122 $this->_saveBigData($iSBDcnt, $aList); 123 // Write PPS 124 $this->_savePps($aList); 125 // Write Big Block Depot and BDList and Adding Header informations 126 $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); 127 // Close File, send it to stdout if necessary 128 if(($filename == '-') or ($filename == '')) 129 { 130 fseek($this->_FILEH_, 0); 131 fpassthru($this->_FILEH_); 132 @fclose($this->_FILEH_); 133 // Delete the temporary file. 134 @unlink($this->_tmp_filename); 135 } 136 else { 137 @fclose($this->_FILEH_); 138 } 139 return true; 140 } 141 142 /** 143 * Calculate some numbers 144 * 145 * @access private 146 * @param array $raList Reference to an array of PPS's 147 * @return array The array of numbers 148 */ 149 function _calcSize(&$raList) 150 { 151 // Calculate Basic Setting 152 list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0); 153 $iSmallLen = 0; 154 $iSBcnt = 0; 155 for ($i = 0; $i < count($raList); $i++) { 156 if($raList[$i]->Type == OLE_PPS_TYPE_FILE) { 157 $raList[$i]->Size = $raList[$i]->_DataLen(); 158 if($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { 159 $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) 160 + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); 161 } 162 else { 163 $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + 164 (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); 165 } 166 } 167 } 168 $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE; 169 $iSlCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); 170 $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0); 171 $iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) + 172 (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0)); 173 $iCnt = count($raList); 174 $iBdCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; 175 $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0)); 176 177 return array($iSBDcnt, $iBBcnt, $iPPScnt); 178 } 179 180 /** 181 * Helper function for caculating a magic value for block sizes 182 * 183 * @access private 184 * @param integer $i2 The argument 185 * @see save() 186 * @return integer 187 */ 188 function _adjust2($i2) 189 { 190 $iWk = log($i2)/log(2); 191 return ($iWk > floor($iWk))? floor($iWk)+1:$iWk; 192 } 193 194 /** 195 * Save OLE header 196 * 197 * @access private 198 * @param integer $iSBDcnt 199 * @param integer $iBBcnt 200 * @param integer $iPPScnt 201 */ 202 function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) 203 { 204 $FILE = $this->_FILEH_; 205 206 // Calculate Basic Setting 207 $iBlCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; 208 $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; 209 210 $iBdExL = 0; 211 $iAll = $iBBcnt + $iPPScnt + $iSBDcnt; 212 $iAllW = $iAll; 213 $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); 214 $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); 215 216 // Calculate BD count 217 if ($iBdCnt >$i1stBdL) 218 { 219 while (1) 220 { 221 $iBdExL++; 222 $iAllW++; 223 $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); 224 $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); 225 if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) { 226 break; 227 } 228 } 229 } 230 231 // Save Header 232 fwrite($FILE, 233 "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" 234 . "\x00\x00\x00\x00" 235 . "\x00\x00\x00\x00" 236 . "\x00\x00\x00\x00" 237 . "\x00\x00\x00\x00" 238 . pack("v", 0x3b) 239 . pack("v", 0x03) 240 . pack("v", -2) 241 . pack("v", 9) 242 . pack("v", 6) 243 . pack("v", 0) 244 . "\x00\x00\x00\x00" 245 . "\x00\x00\x00\x00" 246 . pack("V", $iBdCnt) 247 . pack("V", $iBBcnt+$iSBDcnt) //ROOT START 248 . pack("V", 0) 249 . pack("V", 0x1000) 250 . pack("V", 0) //Small Block Depot 251 . pack("V", 1) 252 ); 253 // Extra BDList Start, Count 254 if ($iBdCnt < $i1stBdL) 255 { 256 fwrite($FILE, 257 pack("V", -2). // Extra BDList Start 258 pack("V", 0) // Extra BDList Count 259 ); 260 } 261 else 262 { 263 fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL)); 264 } 265 266 // BDList 267 for ($i=0; $i<$i1stBdL and $i < $iBdCnt; $i++) { 268 fwrite($FILE, pack("V", $iAll+$i)); 269 } 270 if ($i < $i1stBdL) 271 { 272 for ($j = 0; $j < ($i1stBdL-$i); $j++) { 273 fwrite($FILE, (pack("V", -1))); 274 } 275 } 276 } 277 278 /** 279 * Saving big data (PPS's with data bigger than OLE_DATA_SIZE_SMALL) 280 * 281 * @access private 282 * @param integer $iStBlk 283 * @param array &$raList Reference to array of PPS's 284 */ 285 function _saveBigData($iStBlk, &$raList) 286 { 287 $FILE = $this->_FILEH_; 288 289 // cycle through PPS's 290 for ($i = 0; $i < count($raList); $i++) 291 { 292 if($raList[$i]->Type != OLE_PPS_TYPE_DIR) 293 { 294 $raList[$i]->Size = $raList[$i]->_DataLen(); 295 if(($raList[$i]->Size >= OLE_DATA_SIZE_SMALL) or 296 (($raList[$i]->Type == OLE_PPS_TYPE_ROOT) and isset($raList[$i]->_data))) 297 { 298 // Write Data 299 if(isset($raList[$i]->_PPS_FILE)) 300 { 301 $iLen = 0; 302 fseek($raList[$i]->_PPS_FILE, 0); // To The Top 303 while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) 304 { 305 $iLen += strlen($sBuff); 306 fwrite($FILE, $sBuff); 307 } 308 } 309 else { 310 fwrite($FILE, $raList[$i]->_data); 311 } 312 313 if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) 314 { 315 for ($j = 0; $j < ($this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)); $j++) { 316 fwrite($FILE, "\x00"); 317 } 318 } 319 // Set For PPS 320 $raList[$i]->_StartBlock = $iStBlk; 321 $iStBlk += 322 (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + 323 (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); 324 } 325 // Close file for each PPS, and unlink it 326 if (isset($raList[$i]->_PPS_FILE)) 327 { 328 @fclose($raList[$i]->_PPS_FILE); 329 $raList[$i]->_PPS_FILE = null; 330 @unlink($raList[$i]->_tmp_filename); 331 } 332 } 333 } 334 } 335 336 /** 337 * get small data (PPS's with data smaller than OLE_DATA_SIZE_SMALL) 338 * 339 * @access private 340 * @param array &$raList Reference to array of PPS's 341 */ 342 function _makeSmallData(&$raList) 343 { 344 $sRes = ''; 345 $FILE = $this->_FILEH_; 346 $iSmBlk = 0; 347 348 for ($i = 0; $i < count($raList); $i++) 349 { 350 // Make SBD, small data string 351 if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) 352 { 353 if ($raList[$i]->Size <= 0) { 354 continue; 355 } 356 if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) 357 { 358 $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) 359 + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); 360 // Add to SBD 361 for ($j = 0; $j < ($iSmbCnt-1); $j++) { 362 fwrite($FILE, pack("V", $j+$iSmBlk+1)); 363 } 364 fwrite($FILE, pack("V", -2)); 365 366 // Add to Data String(this will be written for RootEntry) 367 if ($raList[$i]->_PPS_FILE) 368 { 369 fseek($raList[$i]->_PPS_FILE, 0); // To The Top 370 while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { 371 $sRes .= $sBuff; 372 } 373 } 374 else { 375 $sRes .= $raList[$i]->_data; 376 } 377 if($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) 378 { 379 for ($j = 0; $j < ($this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); $j++) { 380 $sRes .= "\x00"; 381 } 382 } 383 // Set for PPS 384 $raList[$i]->_StartBlock = $iSmBlk; 385 $iSmBlk += $iSmbCnt; 386 } 387 } 388 } 389 $iSbCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); 390 if($iSmBlk % $iSbCnt) 391 { 392 for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); $i++) { 393 fwrite($FILE, pack("V", -1)); 394 } 395 } 396 return $sRes; 397 } 398 399 /** 400 * Saves all the PPS's WKs 401 * 402 * @access private 403 * @param array $raList Reference to an array with all PPS's 404 */ 405 function _savePps(&$raList) 406 { 407 // Save each PPS WK 408 for ($i = 0; $i < count($raList); $i++) { 409 fwrite($this->_FILEH_, $raList[$i]->_getPpsWk()); 410 } 411 // Adjust for Block 412 $iCnt = count($raList); 413 $iBCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; 414 if ($iCnt % $iBCnt) 415 { 416 for ($i = 0; $i < (($iBCnt - ($iCnt % $iBCnt)) * OLE_PPS_SIZE); $i++) { 417 fwrite($this->_FILEH_, "\x00"); 418 } 419 } 420 } 421 422 /** 423 * Saving Big Block Depot 424 * 425 * @access private 426 * @param integer $iSbdSize 427 * @param integer $iBsize 428 * @param integer $iPpsCnt 429 */ 430 function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) 431 { 432 $FILE = $this->_FILEH_; 433 // Calculate Basic Setting 434 $iBbCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; 435 $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; 436 437 $iBdExL = 0; 438 $iAll = $iBsize + $iPpsCnt + $iSbdSize; 439 $iAllW = $iAll; 440 $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); 441 $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); 442 // Calculate BD count 443 if ($iBdCnt >$i1stBdL) 444 { 445 while (1) 446 { 447 $iBdExL++; 448 $iAllW++; 449 $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); 450 $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); 451 if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) { 452 break; 453 } 454 } 455 } 456 457 // Making BD 458 // Set for SBD 459 if ($iSbdSize > 0) 460 { 461 for ($i = 0; $i<($iSbdSize-1); $i++) { 462 fwrite($FILE, pack("V", $i+1)); 463 } 464 fwrite($FILE, pack("V", -2)); 465 } 466 // Set for B 467 for ($i = 0; $i<($iBsize-1); $i++) { 468 fwrite($FILE, pack("V", $i+$iSbdSize+1)); 469 } 470 fwrite($FILE, pack("V", -2)); 471 472 // Set for PPS 473 for ($i = 0; $i<($iPpsCnt-1); $i++) { 474 fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1)); 475 } 476 fwrite($FILE, pack("V", -2)); 477 // Set for BBD itself ( 0xFFFFFFFD : BBD) 478 for ($i=0; $i<$iBdCnt;$i++) { 479 fwrite($FILE, pack("V", 0xFFFFFFFD)); 480 } 481 // Set for ExtraBDList 482 for ($i=0; $i<$iBdExL;$i++) { 483 fwrite($FILE, pack("V", 0xFFFFFFFC)); 484 } 485 // Adjust for Block 486 if (($iAllW + $iBdCnt) % $iBbCnt) 487 { 488 for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); $i++) { 489 fwrite($FILE, pack("V", -1)); 490 } 491 } 492 // Extra BDList 493 if ($iBdCnt > $i1stBdL) 494 { 495 $iN=0; 496 $iNb=0; 497 for ($i=$i1stBdL;$i<$iBdCnt; $i++, $iN++) 498 { 499 if ($iN>=($iBbCnt-1)) 500 { 501 $iN = 0; 502 $iNb++; 503 fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb)); 504 } 505 fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i)); 506 } 507 if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) 508 { 509 for ($i = 0; $i < (($iBbCnt-1) - (($iBdCnt-$i1stBdL) % ($iBbCnt-1))); $i++) { 510 fwrite($FILE, pack("V", -1)); 511 } 512 } 513 fwrite($FILE, pack("V", -2)); 514 } 515 } 516} 517