ILIAS  Release_3_10_x_branch Revision 61812
 All Data Structures Namespaces Files Functions Variables Groups Pages
OLE.php
Go to the documentation of this file.
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 4249 2004-06-16 14:14:02Z hschottm $
21 
22 
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 'classes/OLE/PPS.php';
35 
43 class OLE extends PEAR
44 {
50 
55  var $_list;
56 
62  function OLE()
63  {
64  $this->_list = array();
65  }
66 
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 
129  function _OLE()
130  {
131  fclose($this->_file_handle);
132  }
133 
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 
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 
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 
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 
256  function ppsTotal()
257  {
258  return count($this->_list);
259  }
260 
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 
291  function getDataLength($index)
292  {
293  if (isset($this->_list[$index])) {
294  return $this->_list[$index]->Size;
295  }
296  return 0;
297  }
298 
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 
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 
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 ?>