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