~begerega/ihris-chw-sl/trunk

« back to all changes in this revision

Viewing changes to tools/PHPExcel/PHPExcel/Shared/OLE.php

  • Committer: Ese Egerega
  • Date: 2018-05-03 14:17:04 UTC
  • Revision ID: egerega@gmail.com-20180503141704-3br8dto013rgx65x
Initial import of Sierra Leone  CHW registry

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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: OLE.php,v 1.13 2007/03/07 14:38:25 schmidt Exp $
 
21
 
 
22
 
 
23
/**
 
24
* Array for storing OLE instances that are accessed from
 
25
* OLE_ChainedBlockStream::stream_open().
 
26
* @var  array
 
27
*/
 
28
$GLOBALS['_OLE_INSTANCES'] = array();
 
29
 
 
30
/**
 
31
* OLE package base class.
 
32
*
 
33
* @author   Xavier Noguer <xnoguer@php.net>
 
34
* @author   Christian Schmidt <schmidt@php.net>
 
35
* @category   PHPExcel
 
36
* @package    PHPExcel_Shared_OLE
 
37
*/
 
38
class PHPExcel_Shared_OLE
 
39
{
 
40
    const OLE_PPS_TYPE_ROOT   =      5;
 
41
    const OLE_PPS_TYPE_DIR    =      1;
 
42
    const OLE_PPS_TYPE_FILE   =      2;
 
43
    const OLE_DATA_SIZE_SMALL = 0x1000;
 
44
    const OLE_LONG_INT_SIZE   =      4;
 
45
    const OLE_PPS_SIZE        =   0x80;
 
46
 
 
47
    /**
 
48
     * The file handle for reading an OLE container
 
49
     * @var resource
 
50
    */
 
51
    public $_file_handle;
 
52
 
 
53
    /**
 
54
    * Array of PPS's found on the OLE container
 
55
    * @var array
 
56
    */
 
57
    public $_list = array();
 
58
 
 
59
    /**
 
60
     * Root directory of OLE container
 
61
     * @var OLE_PPS_Root
 
62
    */
 
63
    public $root;
 
64
 
 
65
    /**
 
66
     * Big Block Allocation Table
 
67
     * @var array  (blockId => nextBlockId)
 
68
    */
 
69
    public $bbat;
 
70
 
 
71
    /**
 
72
     * Short Block Allocation Table
 
73
     * @var array  (blockId => nextBlockId)
 
74
    */
 
75
    public $sbat;
 
76
 
 
77
    /**
 
78
     * Size of big blocks. This is usually 512.
 
79
     * @var  int  number of octets per block.
 
80
    */
 
81
    public $bigBlockSize;
 
82
 
 
83
    /**
 
84
     * Size of small blocks. This is usually 64.
 
85
     * @var  int  number of octets per block
 
86
    */
 
87
    public $smallBlockSize;
 
88
 
 
89
    /**
 
90
     * Reads an OLE container from the contents of the file given.
 
91
     *
 
92
     * @acces public
 
93
     * @param string $file
 
94
     * @return mixed true on success, PEAR_Error on failure
 
95
    */
 
96
    public function read($file)
 
97
    {
 
98
        $fh = fopen($file, "r");
 
99
        if (!$fh) {
 
100
            throw new PHPExcel_Reader_Exception("Can't open file $file");
 
101
        }
 
102
        $this->_file_handle = $fh;
 
103
 
 
104
        $signature = fread($fh, 8);
 
105
        if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
 
106
            throw new PHPExcel_Reader_Exception("File doesn't seem to be an OLE container.");
 
107
        }
 
108
        fseek($fh, 28);
 
109
        if (fread($fh, 2) != "\xFE\xFF") {
 
110
            // This shouldn't be a problem in practice
 
111
            throw new PHPExcel_Reader_Exception("Only Little-Endian encoding is supported.");
 
112
        }
 
113
        // Size of blocks and short blocks in bytes
 
114
        $this->bigBlockSize = pow(2, self::_readInt2($fh));
 
115
        $this->smallBlockSize  = pow(2, self::_readInt2($fh));
 
116
 
 
117
        // Skip UID, revision number and version number
 
118
        fseek($fh, 44);
 
119
        // Number of blocks in Big Block Allocation Table
 
120
        $bbatBlockCount = self::_readInt4($fh);
 
121
 
 
122
        // Root chain 1st block
 
123
        $directoryFirstBlockId = self::_readInt4($fh);
 
124
 
 
125
        // Skip unused bytes
 
126
        fseek($fh, 56);
 
127
        // Streams shorter than this are stored using small blocks
 
128
        $this->bigBlockThreshold = self::_readInt4($fh);
 
129
        // Block id of first sector in Short Block Allocation Table
 
130
        $sbatFirstBlockId = self::_readInt4($fh);
 
131
        // Number of blocks in Short Block Allocation Table
 
132
        $sbbatBlockCount = self::_readInt4($fh);
 
133
        // Block id of first sector in Master Block Allocation Table
 
134
        $mbatFirstBlockId = self::_readInt4($fh);
 
135
        // Number of blocks in Master Block Allocation Table
 
136
        $mbbatBlockCount = self::_readInt4($fh);
 
137
        $this->bbat = array();
 
138
 
 
139
        // Remaining 4 * 109 bytes of current block is beginning of Master
 
140
        // Block Allocation Table
 
141
        $mbatBlocks = array();
 
142
        for ($i = 0; $i < 109; ++$i) {
 
143
            $mbatBlocks[] = self::_readInt4($fh);
 
144
        }
 
145
 
 
146
        // Read rest of Master Block Allocation Table (if any is left)
 
147
        $pos = $this->_getBlockOffset($mbatFirstBlockId);
 
148
        for ($i = 0; $i < $mbbatBlockCount; ++$i) {
 
149
            fseek($fh, $pos);
 
150
            for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
 
151
                $mbatBlocks[] = self::_readInt4($fh);
 
152
            }
 
153
            // Last block id in each block points to next block
 
154
            $pos = $this->_getBlockOffset(self::_readInt4($fh));
 
155
        }
 
156
 
 
157
        // Read Big Block Allocation Table according to chain specified by
 
158
        // $mbatBlocks
 
159
        for ($i = 0; $i < $bbatBlockCount; ++$i) {
 
160
            $pos = $this->_getBlockOffset($mbatBlocks[$i]);
 
161
            fseek($fh, $pos);
 
162
            for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) {
 
163
                $this->bbat[] = self::_readInt4($fh);
 
164
            }
 
165
        }
 
166
 
 
167
        // Read short block allocation table (SBAT)
 
168
        $this->sbat = array();
 
169
        $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
 
170
        $sbatFh = $this->getStream($sbatFirstBlockId);
 
171
        for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
 
172
            $this->sbat[$blockId] = self::_readInt4($sbatFh);
 
173
        }
 
174
        fclose($sbatFh);
 
175
 
 
176
        $this->_readPpsWks($directoryFirstBlockId);
 
177
 
 
178
        return true;
 
179
    }
 
180
 
 
181
    /**
 
182
     * @param  int  block id
 
183
     * @param  int  byte offset from beginning of file
 
184
     * @access public
 
185
     */
 
186
    public function _getBlockOffset($blockId)
 
187
    {
 
188
        return 512 + $blockId * $this->bigBlockSize;
 
189
    }
 
190
 
 
191
    /**
 
192
    * Returns a stream for use with fread() etc. External callers should
 
193
    * use PHPExcel_Shared_OLE_PPS_File::getStream().
 
194
    * @param   int|PPS   block id or PPS
 
195
    * @return  resource  read-only stream
 
196
    */
 
197
    public function getStream($blockIdOrPps)
 
198
    {
 
199
        static $isRegistered = false;
 
200
        if (!$isRegistered) {
 
201
            stream_wrapper_register('ole-chainedblockstream', 'PHPExcel_Shared_OLE_ChainedBlockStream');
 
202
            $isRegistered = true;
 
203
        }
 
204
 
 
205
        // Store current instance in global array, so that it can be accessed
 
206
        // in OLE_ChainedBlockStream::stream_open().
 
207
        // Object is removed from self::$instances in OLE_Stream::close().
 
208
        $GLOBALS['_OLE_INSTANCES'][] = $this;
 
209
        $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES']));
 
210
 
 
211
        $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
 
212
        if ($blockIdOrPps instanceof PHPExcel_Shared_OLE_PPS) {
 
213
            $path .= '&blockId=' . $blockIdOrPps->_StartBlock;
 
214
            $path .= '&size=' . $blockIdOrPps->Size;
 
215
        } else {
 
216
            $path .= '&blockId=' . $blockIdOrPps;
 
217
        }
 
218
        return fopen($path, 'r');
 
219
    }
 
220
 
 
221
    /**
 
222
     * Reads a signed char.
 
223
     * @param   resource  file handle
 
224
     * @return  int
 
225
     * @access public
 
226
     */
 
227
    private static function _readInt1($fh)
 
228
    {
 
229
        list(, $tmp) = unpack("c", fread($fh, 1));
 
230
        return $tmp;
 
231
    }
 
232
 
 
233
    /**
 
234
     * Reads an unsigned short (2 octets).
 
235
     * @param   resource  file handle
 
236
     * @return  int
 
237
     * @access public
 
238
     */
 
239
    private static function _readInt2($fh)
 
240
    {
 
241
        list(, $tmp) = unpack("v", fread($fh, 2));
 
242
        return $tmp;
 
243
    }
 
244
 
 
245
    /**
 
246
     * Reads an unsigned long (4 octets).
 
247
     * @param   resource  file handle
 
248
     * @return  int
 
249
     * @access public
 
250
     */
 
251
    private static function _readInt4($fh)
 
252
    {
 
253
        list(, $tmp) = unpack("V", fread($fh, 4));
 
254
        return $tmp;
 
255
    }
 
256
 
 
257
    /**
 
258
    * Gets information about all PPS's on the OLE container from the PPS WK's
 
259
    * creates an OLE_PPS object for each one.
 
260
    *
 
261
    * @access public
 
262
    * @param  integer  the block id of the first block
 
263
    * @return mixed true on success, PEAR_Error on failure
 
264
    */
 
265
    public function _readPpsWks($blockId)
 
266
    {
 
267
        $fh = $this->getStream($blockId);
 
268
        for ($pos = 0;; $pos += 128) {
 
269
            fseek($fh, $pos, SEEK_SET);
 
270
            $nameUtf16 = fread($fh, 64);
 
271
            $nameLength = self::_readInt2($fh);
 
272
            $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
 
273
            // Simple conversion from UTF-16LE to ISO-8859-1
 
274
            $name = str_replace("\x00", "", $nameUtf16);
 
275
            $type = self::_readInt1($fh);
 
276
            switch ($type) {
 
277
                case self::OLE_PPS_TYPE_ROOT:
 
278
                    $pps = new PHPExcel_Shared_OLE_PPS_Root(null, null, array());
 
279
                    $this->root = $pps;
 
280
                    break;
 
281
                case self::OLE_PPS_TYPE_DIR:
 
282
                    $pps = new PHPExcel_Shared_OLE_PPS(null, null, null, null, null, null, null, null, null, array());
 
283
                    break;
 
284
                case self::OLE_PPS_TYPE_FILE:
 
285
                    $pps = new PHPExcel_Shared_OLE_PPS_File($name);
 
286
                    break;
 
287
                default:
 
288
                    continue;
 
289
            }
 
290
            fseek($fh, 1, SEEK_CUR);
 
291
            $pps->Type    = $type;
 
292
            $pps->Name    = $name;
 
293
            $pps->PrevPps = self::_readInt4($fh);
 
294
            $pps->NextPps = self::_readInt4($fh);
 
295
            $pps->DirPps  = self::_readInt4($fh);
 
296
            fseek($fh, 20, SEEK_CUR);
 
297
            $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
 
298
            $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
 
299
            $pps->_StartBlock = self::_readInt4($fh);
 
300
            $pps->Size = self::_readInt4($fh);
 
301
            $pps->No = count($this->_list);
 
302
            $this->_list[] = $pps;
 
303
 
 
304
            // check if the PPS tree (starting from root) is complete
 
305
            if (isset($this->root) && $this->_ppsTreeComplete($this->root->No)) {
 
306
                break;
 
307
            }
 
308
        }
 
309
        fclose($fh);
 
310
 
 
311
        // Initialize $pps->children on directories
 
312
        foreach ($this->_list as $pps) {
 
313
            if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
 
314
                $nos = array($pps->DirPps);
 
315
                $pps->children = array();
 
316
                while ($nos) {
 
317
                    $no = array_pop($nos);
 
318
                    if ($no != -1) {
 
319
                        $childPps = $this->_list[$no];
 
320
                        $nos[] = $childPps->PrevPps;
 
321
                        $nos[] = $childPps->NextPps;
 
322
                        $pps->children[] = $childPps;
 
323
                    }
 
324
                }
 
325
            }
 
326
        }
 
327
 
 
328
        return true;
 
329
    }
 
330
 
 
331
    /**
 
332
    * It checks whether the PPS tree is complete (all PPS's read)
 
333
    * starting with the given PPS (not necessarily root)
 
334
    *
 
335
    * @access public
 
336
    * @param integer $index The index of the PPS from which we are checking
 
337
    * @return boolean Whether the PPS tree for the given PPS is complete
 
338
    */
 
339
    public function _ppsTreeComplete($index)
 
340
    {
 
341
        return isset($this->_list[$index]) &&
 
342
               ($pps = $this->_list[$index]) &&
 
343
               ($pps->PrevPps == -1 ||
 
344
                $this->_ppsTreeComplete($pps->PrevPps)) &&
 
345
               ($pps->NextPps == -1 ||
 
346
                $this->_ppsTreeComplete($pps->NextPps)) &&
 
347
               ($pps->DirPps == -1 ||
 
348
                $this->_ppsTreeComplete($pps->DirPps));
 
349
    }
 
350
 
 
351
    /**
 
352
    * Checks whether a PPS is a File PPS or not.
 
353
    * If there is no PPS for the index given, it will return false.
 
354
    *
 
355
    * @access public
 
356
    * @param integer $index The index for the PPS
 
357
    * @return bool true if it's a File PPS, false otherwise
 
358
    */
 
359
    public function isFile($index)
 
360
    {
 
361
        if (isset($this->_list[$index])) {
 
362
            return ($this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE);
 
363
        }
 
364
        return false;
 
365
    }
 
366
 
 
367
    /**
 
368
    * Checks whether a PPS is a Root PPS or not.
 
369
    * If there is no PPS for the index given, it will return false.
 
370
    *
 
371
    * @access public
 
372
    * @param integer $index The index for the PPS.
 
373
    * @return bool true if it's a Root PPS, false otherwise
 
374
    */
 
375
    public function isRoot($index)
 
376
    {
 
377
        if (isset($this->_list[$index])) {
 
378
            return ($this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT);
 
379
        }
 
380
        return false;
 
381
    }
 
382
 
 
383
    /**
 
384
    * Gives the total number of PPS's found in the OLE container.
 
385
    *
 
386
    * @access public
 
387
    * @return integer The total number of PPS's found in the OLE container
 
388
    */
 
389
    public function ppsTotal()
 
390
    {
 
391
        return count($this->_list);
 
392
    }
 
393
 
 
394
    /**
 
395
    * Gets data from a PPS
 
396
    * If there is no PPS for the index given, it will return an empty string.
 
397
    *
 
398
    * @access public
 
399
    * @param integer $index    The index for the PPS
 
400
    * @param integer $position The position from which to start reading
 
401
    *                          (relative to the PPS)
 
402
    * @param integer $length   The amount of bytes to read (at most)
 
403
    * @return string The binary string containing the data requested
 
404
    * @see OLE_PPS_File::getStream()
 
405
    */
 
406
    public function getData($index, $position, $length)
 
407
    {
 
408
        // if position is not valid return empty string
 
409
        if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
 
410
            return '';
 
411
        }
 
412
        $fh = $this->getStream($this->_list[$index]);
 
413
        $data = stream_get_contents($fh, $length, $position);
 
414
        fclose($fh);
 
415
        return $data;
 
416
    }
 
417
 
 
418
    /**
 
419
    * Gets the data length from a PPS
 
420
    * If there is no PPS for the index given, it will return 0.
 
421
    *
 
422
    * @access public
 
423
    * @param integer $index    The index for the PPS
 
424
    * @return integer The amount of bytes in data the PPS has
 
425
    */
 
426
    public function getDataLength($index)
 
427
    {
 
428
        if (isset($this->_list[$index])) {
 
429
            return $this->_list[$index]->Size;
 
430
        }
 
431
        return 0;
 
432
    }
 
433
 
 
434
    /**
 
435
    * Utility function to transform ASCII text to Unicode
 
436
    *
 
437
    * @access public
 
438
    * @static
 
439
    * @param string $ascii The ASCII string to transform
 
440
    * @return string The string in Unicode
 
441
    */
 
442
    public static function Asc2Ucs($ascii)
 
443
    {
 
444
        $rawname = '';
 
445
        for ($i = 0; $i < strlen($ascii); ++$i) {
 
446
            $rawname .= $ascii{$i} . "\x00";
 
447
        }
 
448
        return $rawname;
 
449
    }
 
450
 
 
451
    /**
 
452
    * Utility function
 
453
    * Returns a string for the OLE container with the date given
 
454
    *
 
455
    * @access public
 
456
    * @static
 
457
    * @param integer $date A timestamp
 
458
    * @return string The string for the OLE container
 
459
    */
 
460
    public static function LocalDate2OLE($date = null)
 
461
    {
 
462
        if (!isset($date)) {
 
463
            return "\x00\x00\x00\x00\x00\x00\x00\x00";
 
464
        }
 
465
 
 
466
        // factor used for separating numbers into 4 bytes parts
 
467
        $factor = pow(2, 32);
 
468
 
 
469
        // days from 1-1-1601 until the beggining of UNIX era
 
470
        $days = 134774;
 
471
        // calculate seconds
 
472
        $big_date = $days*24*3600 + gmmktime(date("H", $date), date("i", $date), date("s", $date), date("m", $date), date("d", $date), date("Y", $date));
 
473
        // multiply just to make MS happy
 
474
        $big_date *= 10000000;
 
475
 
 
476
        $high_part = floor($big_date / $factor);
 
477
        // lower 4 bytes
 
478
        $low_part = floor((($big_date / $factor) - $high_part) * $factor);
 
479
 
 
480
        // Make HEX string
 
481
        $res = '';
 
482
 
 
483
        for ($i = 0; $i < 4; ++$i) {
 
484
            $hex = $low_part % 0x100;
 
485
            $res .= pack('c', $hex);
 
486
            $low_part /= 0x100;
 
487
        }
 
488
        for ($i = 0; $i < 4; ++$i) {
 
489
            $hex = $high_part % 0x100;
 
490
            $res .= pack('c', $hex);
 
491
            $high_part /= 0x100;
 
492
        }
 
493
        return $res;
 
494
    }
 
495
 
 
496
    /**
 
497
    * Returns a timestamp from an OLE container's date
 
498
    *
 
499
    * @access public
 
500
    * @static
 
501
    * @param integer $string A binary string with the encoded date
 
502
    * @return string The timestamp corresponding to the string
 
503
    */
 
504
    public static function OLE2LocalDate($string)
 
505
    {
 
506
        if (strlen($string) != 8) {
 
507
            return new PEAR_Error("Expecting 8 byte string");
 
508
        }
 
509
 
 
510
        // factor used for separating numbers into 4 bytes parts
 
511
        $factor = pow(2, 32);
 
512
        list(, $high_part) = unpack('V', substr($string, 4, 4));
 
513
        list(, $low_part) = unpack('V', substr($string, 0, 4));
 
514
 
 
515
        $big_date = ($high_part * $factor) + $low_part;
 
516
        // translate to seconds
 
517
        $big_date /= 10000000;
 
518
 
 
519
        // days from 1-1-1601 until the beggining of UNIX era
 
520
        $days = 134774;
 
521
 
 
522
        // translate to seconds from beggining of UNIX era
 
523
        $big_date -= $days * 24 * 3600;
 
524
        return floor($big_date);
 
525
    }
 
526
}