Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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.7 2003/08/21 15:15:40 xnoguer Exp $
21
 
22
 
23
/**
24
* Constants for OLE package
25
*/
26
define('OLE_PPS_TYPE_ROOT',        5);
27
define('OLE_PPS_TYPE_DIR',         1);
28
define('OLE_PPS_TYPE_FILE',        2);
29
define('OLE_DATA_SIZE_SMALL', 0x1000);
30
define('OLE_LONG_INT_SIZE',        4);
31
define('OLE_PPS_SIZE',          0x80);
32
 
33
require_once('PEAR.php');
34
require_once 'OLE/PPS.php';
35
 
36
/**
37
* OLE package base class.
38
*
39
* @author   Xavier Noguer <xnoguer@php.net>
40
* @category Structures
41
* @package  OLE
42
*/
43
class OLE extends PEAR
44
{
45
    /**
46
    * The file handle for reading an OLE container
47
    * @var resource
48
    */
49
    var $_file_handle;
50
 
51
    /**
52
    * Array of PPS's found on the OLE container
53
    * @var array
54
    */
55
    var $_list;
56
 
57
    /**
58
    * Creates a new OLE object
59
    * Remember to use ampersand when creating an OLE object ($my_ole =& new OLE();)
60
    * @access public
61
    */
62
    function OLE()
63
    {
64
        $this->_list = array();
65
    }
66
 
67
    /**
68
    * Reads an OLE container from the contents of the file given.
69
    *
70
    * @acces public
71
    * @param string $file
72
    * @return mixed true on success, PEAR_Error on failure
73
    */
74
    function read($file)
75
    {
76
        /* consider storing offsets as constants */
77
        $big_block_size_offset = 30;
78
        $iBdbCnt_offset        = 44;
79
        $bd_start_offset       = 68;
80
 
81
        $fh = @fopen($file, "r");
82
        if ($fh == false) {
83
            return $this->raiseError("Can't open file $file");
84
        }
85
        $this->_file_handle = $fh;
86
 
87
        /* begin reading OLE attributes */
88
        fseek($fh, 0);
89
        $signature = fread($fh, 8);
90
        if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
91
            return $this->raiseError("File doesn't seem to be an OLE container.");
92
        }
93
        fseek($fh, $big_block_size_offset);
94
        $packed_array = unpack("v", fread($fh, 2));
95
        $big_block_size   = pow(2, $packed_array['']);
96
 
97
        $packed_array = unpack("v", fread($fh, 2));
98
        $small_block_size = pow(2, $packed_array['']);
99
        $i1stBdL = ($big_block_size - 0x4C) / OLE_LONG_INT_SIZE;
100
 
101
        fseek($fh, $iBdbCnt_offset);
102
        $packed_array = unpack("V", fread($fh, 4));
103
        $iBdbCnt = $packed_array[''];
104
 
105
        $packed_array = unpack("V", fread($fh, 4));
106
        $pps_wk_start = $packed_array[''];
107
 
108
        fseek($fh, $bd_start_offset);
109
        $packed_array = unpack("V", fread($fh, 4));
110
        $bd_start = $packed_array[''];
111
        $packed_array = unpack("V", fread($fh, 4));
112
        $bd_count = $packed_array[''];
113
        $packed_array = unpack("V", fread($fh, 4));
114
        $iAll = $packed_array[''];  // this may be wrong
115
        /* create OLE_PPS objects from */
116
        $ret = $this->_readPpsWks($pps_wk_start, $big_block_size);
117
        if (PEAR::isError($ret)) {
118
            return $ret;
119
        }
120
        return true;
121
    }
122
 
123
    /**
124
    * Destructor (using PEAR)
125
    * Just closes the file handle on the OLE file.
126
    *
127
    * @access private
128
    */
129
    function _OLE()
130
    {
131
        fclose($this->_file_handle);
132
    }
133
 
134
    /**
135
    * Gets information about all PPS's on the OLE container from the PPS WK's
136
    * creates an OLE_PPS object for each one.
137
    *
138
    * @access private
139
    * @param integer $pps_wk_start   Position inside the OLE file where PPS WK's start
140
    * @param integer $big_block_size Size of big blobks in the OLE file
141
    * @return mixed true on success, PEAR_Error on failure
142
    */
143
    function _readPpsWks($pps_wk_start, $big_block_size)
144
    {
145
        $pointer = ($pps_wk_start + 1) * $big_block_size;
146
        while (1)
147
        {
148
            fseek($this->_file_handle, $pointer);
149
            $pps_wk = fread($this->_file_handle, OLE_PPS_SIZE);
150
            if (strlen($pps_wk) != OLE_PPS_SIZE) {
151
                break; // Excel likes to add a trailing byte sometimes
152
                //return $this->raiseError("PPS at $pointer seems too short: ".strlen($pps_wk));
153
            }
154
            $name_length = unpack("c", substr($pps_wk, 64, 2)); // FIXME (2 bytes??)
155
            $name_length = $name_length[''] - 2;
156
            $name = substr($pps_wk, 0, $name_length);
157
            $type = unpack("c", substr($pps_wk, 66, 1));
158
            if (($type[''] != OLE_PPS_TYPE_ROOT) and
159
                ($type[''] != OLE_PPS_TYPE_DIR) and
160
                ($type[''] != OLE_PPS_TYPE_FILE))
161
            {
162
                return $this->raiseError("PPS at $pointer has unknown type: {$type['']}");
163
            }
164
            $prev = unpack("V", substr($pps_wk, 68, 4));
165
            $next = unpack("V", substr($pps_wk, 72, 4));
166
            $dir  = unpack("V", substr($pps_wk, 76, 4));
167
            // there is no magic number, it can take different values.
168
            //$magic = unpack("V", strrev(substr($pps_wk, 92, 4)));
169
            $time_1st = substr($pps_wk, 100, 8);
170
            $time_2nd = substr($pps_wk, 108, 8);
171
            $start_block = unpack("V", substr($pps_wk, 116, 4));
172
            $size = unpack("V", substr($pps_wk, 120, 4));
173
            // _data member will point to position in file!!
174
            // OLE_PPS object is created with an empty children array!!
175
            $this->_list[] = new OLE_PPS(null, '', $type[''], $prev[''], $next[''],
176
                                         $dir[''], OLE::OLE2LocalDate($time_1st),
177
                                         OLE::OLE2LocalDate($time_2nd),
178
                                         ($start_block[''] + 1) * $big_block_size, array());
179
            // give it a size
180
            $this->_list[count($this->_list) - 1]->Size = $size[''];
181
            // check if the PPS tree (starting from root) is complete
182
            if ($this->_ppsTreeComplete(0)) {
183
                break;
184
            }
185
            $pointer += OLE_PPS_SIZE;
186
        }
187
    }
188
 
189
    /**
190
    * It checks whether the PPS tree is complete (all PPS's read)
191
    * starting with the given PPS (not necessarily root)
192
    *
193
    * @access private
194
    * @param integer $index The index of the PPS from which we are checking
195
    * @return boolean Whether the PPS tree for the given PPS is complete
196
    */
197
    function _ppsTreeComplete($index)
198
    {
199
        if ($this->_list[$index]->NextPps != -1) {
200
            if (!isset($this->_list[$this->_list[$index]->NextPps])) {
201
                return false;
202
            }
203
            else {
204
                return $this->_ppsTreeComplete($this->_list[$index]->NextPps);
205
            }
206
        }
207
        if ($this->_list[$index]->DirPps != -1) {
208
            if (!isset($this->_list[$this->_list[$index]->DirPps])) {
209
                return false;
210
            }
211
            else {
212
                return $this->_ppsTreeComplete($this->_list[$index]->DirPps);
213
            }
214
        }
215
        return true;
216
    }
217
 
218
    /**
219
    * Checks whether a PPS is a File PPS or not.
220
    * If there is no PPS for the index given, it will return false.
221
    *
222
    * @access public
223
    * @param integer $index The index for the PPS
224
    * @return bool true if it's a File PPS, false otherwise
225
    */
226
    function isFile($index)
227
    {
228
        if (isset($this->_list[$index])) {
229
            return ($this->_list[$index]->Type == OLE_PPS_TYPE_FILE);
230
        }
231
        return false;
232
    }
233
 
234
    /**
235
    * Checks whether a PPS is a Root PPS or not.
236
    * If there is no PPS for the index given, it will return false.
237
    *
238
    * @access public
239
    * @param integer $index The index for the PPS.
240
    * @return bool true if it's a Root PPS, false otherwise
241
    */
242
    function isRoot($index)
243
    {
244
        if (isset($this->_list[$index])) {
245
            return ($this->_list[$index]->Type == OLE_PPS_TYPE_ROOT);
246
        }
247
        return false;
248
    }
249
 
250
    /**
251
    * Gives the total number of PPS's found in the OLE container.
252
    *
253
    * @access public
254
    * @return integer The total number of PPS's found in the OLE container
255
    */
256
    function ppsTotal()
257
    {
258
        return count($this->_list);
259
    }
260
 
261
    /**
262
    * Gets data from a PPS
263
    * If there is no PPS for the index given, it will return an empty string.
264
    *
265
    * @access public
266
    * @param integer $index    The index for the PPS
267
    * @param integer $position The position from which to start reading
268
    *                          (relative to the PPS)
269
    * @param integer $length   The amount of bytes to read (at most)
270
    * @return string The binary string containing the data requested
271
    */
272
    function getData($index, $position, $length)
273
    {
274
        // if position is not valid return empty string
275
        if (!isset($this->_list[$index]) or ($position >= $this->_list[$index]->Size) or ($position < 0)) {
276
            return '';
277
        }
278
        // Beware!!! _data member is actually a position
279
        fseek($this->_file_handle, $this->_list[$index]->_data + $position);
280
        return fread($this->_file_handle, $length);
281
    }
282
 
283
    /**
284
    * Gets the data length from a PPS
285
    * If there is no PPS for the index given, it will return 0.
286
    *
287
    * @access public
288
    * @param integer $index    The index for the PPS
289
    * @return integer The amount of bytes in data the PPS has
290
    */
291
    function getDataLength($index)
292
    {
293
        if (isset($this->_list[$index])) {
294
            return $this->_list[$index]->Size;
295
        }
296
        return 0;
297
    }
298
 
299
    /**
300
    * Utility function to transform ASCII text to Unicode
301
    *
302
    * @access public
303
    * @static
304
    * @param string $ascii The ASCII string to transform
305
    * @return string The string in Unicode
306
    */
307
    function Asc2Ucs($ascii)
308
    {
309
        $rawname = '';
310
        for ($i = 0; $i < strlen($ascii); $i++) {
311
            $rawname .= $ascii{$i}."\x00";
312
        }
313
        return $rawname;
314
    }
315
 
316
    /**
317
    * Utility function
318
    * Returns a string for the OLE container with the date given
319
    *
320
    * @access public
321
    * @static
322
    * @param integer $date A timestamp
323
    * @return string The string for the OLE container
324
    */
325
    function LocalDate2OLE($date = null)
326
    {
327
        if (!isset($date)) {
328
            return "\x00\x00\x00\x00\x00\x00\x00\x00";
329
        }
330
 
331
        // factor used for separating numbers into 4 bytes parts
332
        $factor = pow(2,32);
333
 
334
        // days from 1-1-1601 until the beggining of UNIX era
335
        $days = 134774;
336
        // calculate seconds
337
        $big_date = $days*24*3600 + gmmktime(date("H",$date),date("i",$date),date("s",$date),
338
                                             date("m",$date),date("d",$date),date("Y",$date));
339
        // multiply just to make MS happy
340
        $big_date *= 10000000;
341
 
342
        $high_part = floor($big_date/$factor);
343
        // lower 4 bytes
344
        $low_part = floor((($big_date/$factor) - $high_part)*$factor);
345
 
346
        // Make HEX string
347
        $res = '';
348
 
349
        for ($i=0; $i<4; $i++)
350
        {
351
            $hex = $low_part % 0x100;
352
            $res .= pack('c', $hex);
353
            $low_part /= 0x100;
354
        }
355
        for ($i=0; $i<4; $i++)
356
        {
357
            $hex = $high_part % 0x100;
358
            $res .= pack('c', $hex);
359
            $high_part /= 0x100;
360
        }
361
        return $res;
362
    }
363
 
364
    /**
365
    * Returns a timestamp from an OLE container's date
366
    *
367
    * @access public
368
    * @static
369
    * @param integer $string A binary string with the encoded date
370
    * @return string The timestamp corresponding to the string
371
    */
372
    function OLE2LocalDate($string)
373
    {
374
        if (strlen($string) != 8) {
375
            return new PEAR_Error("Expecting 8 byte string");
376
        }
377
 
378
        // factor used for separating numbers into 4 bytes parts
379
        $factor = pow(2,32);
380
        $high_part = 0;
381
        for ($i=0; $i<4; $i++)
382
        {
383
            $al = unpack('C', $string{(7 - $i)});
384
            $high_part += $al[''];
385
            if ($i < 3) {
386
                $high_part *= 0x100;
387
            }
388
        }
389
        $low_part = 0;
390
        for ($i=4; $i<8; $i++)
391
        {
392
            $al = unpack('C', $string{(7 - $i)});
393
            $low_part += $al[''];
394
            if ($i < 7) {
395
                $low_part *= 0x100;
396
            }
397
        }
398
        $big_date = ($high_part*$factor) + $low_part;
399
        // translate to seconds
400
        $big_date /= 10000000;
401
 
402
        // days from 1-1-1601 until the beggining of UNIX era
403
        $days = 134774;
404
 
405
        // translate to seconds from beggining of UNIX era
406
        $big_date -= $days*24*3600;
407
        return floor($big_date);
408
    }
409
}
410
?>