diff --git a/OLE.php b/OLE.php index e25b5ae..dba5d5e 100755 --- a/OLE.php +++ b/OLE.php @@ -23,12 +23,22 @@ /** * Constants for OLE package */ -define('OLE_PPS_TYPE_ROOT', 5); -define('OLE_PPS_TYPE_DIR', 1); -define('OLE_PPS_TYPE_FILE', 2); -define('OLE_DATA_SIZE_SMALL', 0x1000); -define('OLE_LONG_INT_SIZE', 4); -define('OLE_PPS_SIZE', 0x80); +define('OLE_PPS_TYPE_ROOT', 0x05); +define('OLE_PPS_TYPE_DIR', 0x01); +define('OLE_PPS_TYPE_FILE', 0x02); +define('OLE_DATA_SIZE_SMALL', 0x1000); +define('OLE_LONG_INT_SIZE', 4); +define('OLE_PPS_SIZE', 0x80); +define('OLE_DIFSECT', 0xFFFFFFFC); +define('OLE_FATSECT', 0xFFFFFFFD); +define('OLE_ENDOFCHAIN', 0xFFFFFFFE); +define('OLE_FREESECT', 0xFFFFFFFF); +define('OLE_LITTLE_ENDIAN', 0xFFFE); +define('OLE_VERSION_MAJOR_3', 0x0003); +define('OLE_VERSION_MINOR', 0x003E); +define('OLE_SECTOR_SHIFT_3', 0x0009); +define('OLE_MINI_SECTOR_SHIFT', 0x0006); +define('OLE_CFB_SIGNATURE', "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"); if (!class_exists('PEAR')) { require_once 'PEAR.php'; @@ -133,14 +143,27 @@ function read($file) if (!$fh) { return $this->raiseError("Can't open file $file"); } + + return $this->readStream($fh); + } + + /** + * Reads an OLE container from the contents of the stream given. + * + * @access public + * @param resource $fh + * @return mixed true on success, PEAR_Error on failure + */ + function readStream($fh) + { $this->_file_handle = $fh; $signature = fread($fh, 8); - if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) { + if (OLE_CFB_SIGNATURE != $signature) { return $this->raiseError("File doesn't seem to be an OLE container."); } fseek($fh, 28); - if (fread($fh, 2) != "\xFE\xFF") { + if ($this->_readInt2($fh) != OLE_LITTLE_ENDIAN) { // This shouldn't be a problem in practice return $this->raiseError("Only Little-Endian encoding is supported."); } @@ -165,7 +188,7 @@ function read($file) // Number of blocks in Short Block Allocation Table $sbbatBlockCount = $this->_readInt4($fh); // Block id of first sector in Master Block Allocation Table - $mbatFirstBlockId = $this->_readSignedInt4($fh); + $mbatFirstBlockId = $this->_readInt4($fh); // Number of blocks in Master Block Allocation Table $mbbatBlockCount = $this->_readInt4($fh); $this->bbat = array(); @@ -174,7 +197,7 @@ function read($file) // Block Allocation Table $mbatBlocks = array(); for ($i = 0; $i < 109; $i++) { - $mbatBlocks[] = $this->_readSignedInt4($fh); + $mbatBlocks[] = $this->_readInt4($fh); } // Read rest of Master Block Allocation Table (if any is left) @@ -182,19 +205,24 @@ function read($file) for ($i = 0; $i < $mbbatBlockCount; $i++) { fseek($fh, $pos); for ($j = 0; $j < $this->bigBlockSize / 4 - 1; $j++) { - $mbatBlocks[] = $this->_readInt4($fh); + $mbatBlocks[] = $this->_readInt4($fh); // ffix - invalid block address check } // Last block id in each block points to next block - $pos = $this->_getBlockOffset($this->_readInt4($fh)); + $chainBlock = $this->_readInt4($fh); + if ($chainBlock === OLE_ENDOFCHAIN) { // ENDOFCHAIN + break; + } + $pos = $this->_getBlockOffset($chainBlock); } + // Read Big Block Allocation Table according to chain specified by // $mbatBlocks for ($i = 0; $i < $bbatBlockCount; $i++) { $pos = $this->_getBlockOffset($mbatBlocks[$i]); fseek($fh, $pos); for ($j = 0 ; $j < $this->bigBlockSize / 4; $j++) { - $this->bbat[] = $this->_readSignedInt4($fh); + $this->bbat[] = $this->_readInt4($fh); } } @@ -207,8 +235,9 @@ function read($file) // missing return false; } + for ($blockId = 0; $blockId < $shortBlockCount; $blockId++) { - $this->sbat[$blockId] = $this->_readSignedInt4($sbatFh); + $this->sbat[$blockId] = $this->_readInt4($sbatFh); } fclose($sbatFh); @@ -296,27 +325,6 @@ function _readInt4($fh) return $tmp; } - /** - * Reads a signed long (4 octets). - * @param resource file handle - * @return int - * @access private - */ - function _readSignedInt4($fh) - { - $tmp = $this->_readInt4($fh); - - if (PHP_INT_SIZE == 4) { - // will overflow into a proper value - return $tmp; - } - - // L stands for unsigned long, l for signed long - list(, $tmp) = unpack("s", pack("L", $tmp)); - - return $tmp; - } - /** * Gets information about all PPS's on the OLE container from the PPS WK's * creates an OLE_PPS object for each one. @@ -353,16 +361,16 @@ function _readPpsWks($blockId) default: continue 2; } - fseek($fh, 1, SEEK_CUR); + fseek($fh, 1, SEEK_CUR); // skip Color Flag $pps->Type = $type; $pps->Name = $name; - $pps->PrevPps = $this->_readSignedInt4($fh); - $pps->NextPps = $this->_readSignedInt4($fh); - $pps->DirPps = $this->_readSignedInt4($fh); - fseek($fh, 20, SEEK_CUR); + $pps->PrevPps = $this->_readInt4($fh); // Left Sibling ID + $pps->NextPps = $this->_readInt4($fh); // Right Sibling ID + $pps->DirPps = $this->_readInt4($fh); // Child ID + fseek($fh, 20, SEEK_CUR); // skip CLSID (16 bytes) + State Bits $pps->Time1st = OLE::OLE2LocalDate(fread($fh, 8)); $pps->Time2nd = OLE::OLE2LocalDate(fread($fh, 8)); - $pps->_StartBlock = $this->_readSignedInt4($fh); + $pps->_StartBlock = $this->_readInt4($fh); $pps->Size = $this->_readInt4($fh); $pps->No = count($this->_list); $this->_list[] = $pps; @@ -387,7 +395,7 @@ function _readPpsWks($blockId) $pps->children = array(); while ($nos) { $no = array_pop($nos); - if ($no != -1) { + if ($no != OLE_FREESECT) { $childPps = $this->_list[$no]; $nos[] = $childPps->PrevPps; $nos[] = $childPps->NextPps; @@ -412,11 +420,11 @@ function _ppsTreeComplete($index) { return isset($this->_list[$index]) && ($pps = $this->_list[$index]) && - ($pps->PrevPps == -1 || + ($pps->PrevPps == OLE_FREESECT || $this->_ppsTreeComplete($pps->PrevPps)) && - ($pps->NextPps == -1 || + ($pps->NextPps == OLE_FREESECT || $this->_ppsTreeComplete($pps->NextPps)) && - ($pps->DirPps == -1 || + ($pps->DirPps == OLE_FREESECT || $this->_ppsTreeComplete($pps->DirPps)); } @@ -606,4 +614,3 @@ static function OLE2LocalDate($string) return floor($big_date); } } -?> diff --git a/OLE/ChainedBlockStream.php b/OLE/ChainedBlockStream.php index 45c415d..353f36d 100644 --- a/OLE/ChainedBlockStream.php +++ b/OLE/ChainedBlockStream.php @@ -110,7 +110,7 @@ function stream_open($path, $mode, $options, &$openedPath) // Block id refers to small blocks $rootPos = 0; - while ($blockId != -2) { + while ($blockId != OLE_ENDOFCHAIN) { $pos = $rootPos + $blockId * $this->ole->smallBlockSize; $blockId = $this->ole->sbat[$blockId]; @@ -119,7 +119,7 @@ function stream_open($path, $mode, $options, &$openedPath) } } else { // Block id refers to big blocks - while ($blockId != -2) { + while ($blockId != OLE_ENDOFCHAIN) { $pos = $this->ole->_getBlockOffset($blockId); fseek($this->ole->_file_handle, $pos); $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); @@ -139,7 +139,6 @@ function stream_open($path, $mode, $options, &$openedPath) /** * Implements support for fclose(). - * @return string */ function stream_close() { @@ -204,7 +203,7 @@ function stream_seek($offset, $whence) $this->pos = $offset; } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) { $this->pos += $offset; - } elseif ($whence == SEEK_END && -$offset <= sizeof($this->data)) { + } elseif ($whence == SEEK_END && -$offset <= strlen($this->data)) { $this->pos = strlen($this->data) + $offset; } else { return false; @@ -245,5 +244,3 @@ function stream_flush() // bool dir_rewinddir ( void ) // bool dir_closedir ( void ) } - -?> diff --git a/OLE/PPS.php b/OLE/PPS.php index 3efeb1b..e83719e 100644 --- a/OLE/PPS.php +++ b/OLE/PPS.php @@ -213,15 +213,15 @@ function _getPpsWk() static function _savePpsSetPnt(&$raList, $to_save, $depth = 0) { if ( !is_array($to_save) || (count($to_save) == 0) ) { - return 0xFFFFFFFF; + return OLE_FREESECT; } elseif( count($to_save) == 1 ) { $cnt = count($raList); // If the first entry, it's the root... Don't clone it! $raList[$cnt] = ( $depth == 0 ) ? $to_save[0] : clone $to_save[0]; $raList[$cnt]->No = $cnt; - $raList[$cnt]->PrevPps = 0xFFFFFFFF; - $raList[$cnt]->NextPps = 0xFFFFFFFF; + $raList[$cnt]->PrevPps = OLE_FREESECT; + $raList[$cnt]->NextPps = OLE_FREESECT; $raList[$cnt]->DirPps = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++); return $cnt; } @@ -242,4 +242,3 @@ static function _savePpsSetPnt(&$raList, $to_save, $depth = 0) } } } -?> diff --git a/OLE/PPS/File.php b/OLE/PPS/File.php index 45299d8..a08856b 100644 --- a/OLE/PPS/File.php +++ b/OLE/PPS/File.php @@ -72,7 +72,7 @@ function __construct($name) * * @access public * @param string $dir The dir to be used as temp dir - * @return true if given dir is valid, false otherwise + * @return boolean true if given dir is valid, false otherwise */ function setTempDir($dir) { @@ -128,4 +128,3 @@ function getStream() $this->ole->getStream($this); } } -?> diff --git a/OLE/PPS/Root.php b/OLE/PPS/Root.php index 762e518..a2e423f 100755 --- a/OLE/PPS/Root.php +++ b/OLE/PPS/Root.php @@ -159,8 +159,7 @@ function save($filename) function _calcSize(&$raList) { // Calculate Basic Setting - list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0); - $iSmallLen = 0; + $iBBcnt = 0; $iSBcnt = 0; for ($i = 0; $i < count($raList); $i++) { if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { @@ -240,30 +239,26 @@ function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) // Save Header fwrite($FILE, - "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . pack("v", 0x3b) - . pack("v", 0x03) - . pack("v", -2) - . pack("v", 9) - . pack("v", 6) - . pack("v", 0) - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . pack("V", $iBdCnt) - . pack("V", $iBBcnt+$iSBDcnt) //ROOT START - . pack("V", 0) - . pack("V", 0x1000) - . pack("V", $iSBDcnt ? 0 : -2) //Small Block Depot - . pack("V", $iSBDcnt) + OLE_CFB_SIGNATURE // Header Signature (8 bytes) + . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Header CLSID (16 bytes) + . pack("v", OLE_VERSION_MINOR) // Minor Version (2 bytes) + . pack("v", OLE_VERSION_MAJOR_3) // Major Version (2 bytes) + . pack("v", OLE_LITTLE_ENDIAN) // Byte Order (2 bytes) + . pack("v", OLE_SECTOR_SHIFT_3) // Sector Shift (2 bytes) + . pack("v", OLE_MINI_SECTOR_SHIFT) // Mini Sector Shift (2 bytes) + . "\x00\x00\x00\x00\x00\x00" // Reserved (6 bytes) + . "\x00\x00\x00\x00" // Number of Directory Sectors (4 bytes) + . pack("V", $iBdCnt) // Number of FAT Sectors (4 bytes) + . pack("V", $iBBcnt+$iSBDcnt) //ROOT START, First Directory Sector Location (4 bytes) + . pack("V", 0) // Transaction Signature Number (4 bytes) + . pack("V", 0x00001000) // Mini Stream Cutoff Size (4 bytes) + . pack("V", $iSBDcnt ? 0 : OLE_ENDOFCHAIN) // First Mini FAT Sector Location (4 bytes) + . pack("V", $iSBDcnt) // Number of Mini FAT Sectors (4 bytes) ); // Extra BDList Start, Count if ($iBdCnt < $i1stBdL) { fwrite($FILE, - pack("V", -2). // Extra BDList Start + pack("V", OLE_ENDOFCHAIN). // Extra BDList Start pack("V", 0) // Extra BDList Count ); } else { @@ -274,9 +269,9 @@ function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; $i++) { fwrite($FILE, pack("V", $iAll+$i)); } - if ($i < $i1stBdL) { + if ($i < $i1stBdL) { // free sectors for ($j = 0; $j < ($i1stBdL-$i); $j++) { - fwrite($FILE, (pack("V", -1))); + fwrite($FILE, (pack("V", OLE_FREESECT))); } } } @@ -357,7 +352,7 @@ function _makeSmallData(&$raList) for ($j = 0; $j < ($iSmbCnt-1); $j++) { fwrite($FILE, pack("V", $j+$iSmBlk+1)); } - fwrite($FILE, pack("V", -2)); + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); // Add to Data String(this will be written for RootEntry) if ($raList[$i]->_PPS_FILE) { @@ -382,7 +377,7 @@ function _makeSmallData(&$raList) $iSbCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); if ($iSmBlk % $iSbCnt) { for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); $i++) { - fwrite($FILE, pack("V", -1)); + fwrite($FILE, pack("V", OLE_FREESECT)); } } return $sRes; @@ -452,31 +447,31 @@ function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) for ($i = 0; $i < ($iSbdSize - 1); $i++) { fwrite($FILE, pack("V", $i+1)); } - fwrite($FILE, pack("V", -2)); + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); } // Set for B for ($i = 0; $i < ($iBsize - 1); $i++) { fwrite($FILE, pack("V", $i+$iSbdSize+1)); } - fwrite($FILE, pack("V", -2)); + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); // Set for PPS for ($i = 0; $i < ($iPpsCnt - 1); $i++) { fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1)); } - fwrite($FILE, pack("V", -2)); + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); // Set for BBD itself ( 0xFFFFFFFD : BBD) for ($i = 0; $i < $iBdCnt; $i++) { - fwrite($FILE, pack("V", 0xFFFFFFFD)); + fwrite($FILE, pack("V", OLE_FATSECT)); } // Set for ExtraBDList for ($i = 0; $i < $iBdExL; $i++) { - fwrite($FILE, pack("V", 0xFFFFFFFC)); + fwrite($FILE, pack("V", OLE_DIFSECT)); } // Adjust for Block if (($iAllW + $iBdCnt) % $iBbCnt) { for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); $i++) { - fwrite($FILE, pack("V", -1)); + fwrite($FILE, pack("V", OLE_FREESECT)); } } // Extra BDList @@ -493,10 +488,10 @@ function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) } if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) { for ($i = 0; $i < (($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1))); $i++) { - fwrite($FILE, pack("V", -1)); + fwrite($FILE, pack("V", OLE_FREESECT)); } } - fwrite($FILE, pack("V", -2)); + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); } } @@ -522,22 +517,22 @@ function _create_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks { for($i = 0; $i<($num_sb_blocks-1); $i++) $data .= pack("V", $i+1); - $data .= pack("V", -2); + $data .= pack("V", OLE_ENDOFCHAIN); } for($i = 0; $i<($num_bb_blocks-1); $i++) $data .= pack("V", $i + $num_sb_blocks + 1); - $data .= pack("V", -2); + $data .= pack("V", OLE_ENDOFCHAIN); for($i = 0; $i<($num_pps_blocks-1); $i++) $data .= pack("V", $i + $num_sb_blocks + $num_bb_blocks + 1); - $data .= pack("V", -2); + $data .= pack("V", OLE_ENDOFCHAIN); for($i = 0; $i < $bbd_info["0xFFFFFFFD_blockchain_entries"]; $i++) - $data .= pack("V", 0xFFFFFFFD); + $data .= pack("V", OLE_FATSECT); for($i = 0; $i < $bbd_info["0xFFFFFFFC_blockchain_entries"]; $i++) - $data .= pack("V", 0xFFFFFFFC); + $data .= pack("V", OLE_DIFSECT); // Adjust for Block $all_entries = $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]; @@ -545,7 +540,7 @@ function _create_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks { $rest = $bbd_info["entries_per_block"] - ($all_entries % $bbd_info["entries_per_block"]); for($i = 0; $i < $rest; $i++) - $data .= pack("V", -1); + $data .= pack("V", OLE_FREESECT); } // Extra BDList @@ -570,10 +565,10 @@ function _create_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks { $rest = ($bbd_info["entries_per_block"] - 1) - ($all_entries % ($bbd_info["entries_per_block"] - 1)); for($i = 0; $i < $rest; $i++) - $data .= pack("V", -1); + $data .= pack("V", OLE_FREESECT); } - $data .= pack("V", -2); + $data .= pack("V", OLE_ENDOFCHAIN); } /* @@ -600,38 +595,28 @@ function _create_header($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) // Save Header fwrite($FILE, - "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . pack("v", 0x3b) - . pack("v", 0x03) - . pack("v", -2) - . pack("v", 9) - . pack("v", 6) - . pack("v", 0) - . "\x00\x00\x00\x00" - . "\x00\x00\x00\x00" - . pack("V", $bbd_info["blockchain_list_entries"]) - . pack("V", $num_sb_blocks + $num_bb_blocks) //ROOT START - . pack("V", 0) - . pack("V", 0x1000) - ); - - //Small Block Depot - if($num_sb_blocks > 0) - fwrite($FILE, pack("V", 0)); - else - fwrite($FILE, pack("V", -2)); - - fwrite($FILE, pack("V", $num_sb_blocks)); + OLE_CFB_SIGNATURE // Header Signature (8 bytes) + . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Header CLSID (16 bytes) + . pack("v", OLE_VERSION_MINOR) // Minor Version (2 bytes) + . pack("v", OLE_VERSION_MAJOR_3) // Major Version (2 bytes) + . pack("v", OLE_LITTLE_ENDIAN) // Byte Order (2 bytes) + . pack("v", OLE_SECTOR_SHIFT_3) // Sector Shift (2 bytes) + . pack("v", OLE_MINI_SECTOR_SHIFT) // Mini Sector Shift (2 bytes) + . "\x00\x00\x00\x00\x00\x00" // Reserved (6 bytes) + . "\x00\x00\x00\x00" // Number of Directory Sectors (4 bytes) + . pack("V", $bbd_info["blockchain_list_entries"]) // Number of FAT Sectors (4 bytes) + . pack("V", $num_sb_blocks + $num_bb_blocks) //ROOT START, First Directory Sector Location (4 bytes) + . pack("V", 0) // Transaction Signature Number (4 bytes) + . pack("V", 0x00001000) // Mini Stream Cutoff Size (4 bytes) + . pack("V", $num_sb_blocks > 0 ? 0 : OLE_ENDOFCHAIN) // First Mini FAT Sector Location (4 bytes) + . pack("V", $num_sb_blocks) // Number of Mini FAT Sectors (4 bytes) + ); // Extra BDList Start, Count if($bbd_info["blockchain_list_entries"] < $bbd_info["header_blockchain_list_entries"]) { fwrite($FILE, - pack("V", -2). // Extra BDList Start + pack("V", OLE_ENDOFCHAIN). // Extra BDList Start pack("V", 0) // Extra BDList Count ); } @@ -650,7 +635,7 @@ function _create_header($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) { for($j = 0; $j < ($bbd_info["header_blockchain_list_entries"]-$i); $j++) { - fwrite($FILE, (pack("V", -1))); + fwrite($FILE, (pack("V", OLE_FREESECT))); } } } @@ -713,7 +698,6 @@ function get_number_of_pointer_blocks($num_pointers) function dump($data, $from, $to) { $chars = array(); - $i = 0; for($i = $from; $i < $to; $i++) { if(sizeof($chars) == 16) @@ -735,9 +719,6 @@ function dump($data, $from, $to) foreach($chars as $char) printf(" %02X", $char); print " |\n"; - - $chars = array(); } } } -?> diff --git a/tests/data/Example.bin b/tests/data/Example.bin index 82d09a6..f7eb26e 100644 Binary files a/tests/data/Example.bin and b/tests/data/Example.bin differ