| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sot.hxx" |
| |
| #include <string.h> // memset(), memcpy() |
| #include <rtl/ustring.hxx> |
| #include <com/sun/star/lang/Locale.hpp> |
| #include <unotools/charclass.hxx> |
| #include "sot/stg.hxx" |
| #include "stgelem.hxx" |
| #include "stgcache.hxx" |
| #include "stgstrms.hxx" |
| #include "stgdir.hxx" |
| #include "stgio.hxx" |
| |
| static sal_uInt8 cStgSignature[ 8 ] = { 0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1 }; |
| |
| ////////////////////////////// struct ClsId ///////////////////////////// |
| |
| SvStream& operator >>( SvStream& r, ClsId& rId ) |
| { |
| r >> rId.n1 |
| >> rId.n2 |
| >> rId.n3 |
| >> rId.n4 |
| >> rId.n5 |
| >> rId.n6 |
| >> rId.n7 |
| >> rId.n8 |
| >> rId.n9 |
| >> rId.n10 |
| >> rId.n11; |
| return r; |
| } |
| |
| SvStream& operator <<( SvStream& r, const ClsId& rId ) |
| { |
| return |
| r << (sal_Int32) rId.n1 |
| << (sal_Int16) rId.n2 |
| << (sal_Int16) rId.n3 |
| << (sal_uInt8) rId.n4 |
| << (sal_uInt8) rId.n5 |
| << (sal_uInt8) rId.n6 |
| << (sal_uInt8) rId.n7 |
| << (sal_uInt8) rId.n8 |
| << (sal_uInt8) rId.n9 |
| << (sal_uInt8) rId.n10 |
| << (sal_uInt8) rId.n11; |
| } |
| |
| ///////////////////////////// class StgHeader //////////////////////////// |
| |
| StgHeader::StgHeader() |
| : nVersion( 0 ) |
| , nByteOrder( 0 ) |
| , nPageSize( 0 ) |
| , nDataPageSize( 0 ) |
| , bDirty( 0 ) |
| , nFATSize( 0 ) |
| , nTOCstrm( 0 ) |
| , nReserved( 0 ) |
| , nThreshold( 0 ) |
| , nDataFAT( 0 ) |
| , nDataFATSize( 0 ) |
| , nMasterChain( 0 ) |
| , nMaster( 0 ) |
| { |
| memset( cSignature, 0, sizeof( cSignature ) ); |
| memset( &aClsId, 0, sizeof( ClsId ) ); |
| memset( cReserved, 0, sizeof( cReserved ) ); |
| memset( nMasterFAT, 0, sizeof( nMasterFAT ) ); |
| } |
| |
| void StgHeader::Init() |
| { |
| memcpy( cSignature, cStgSignature, 8 ); |
| memset( &aClsId, 0, sizeof( ClsId ) ); |
| nVersion = 0x0003003B; |
| nByteOrder = 0xFFFE; |
| nPageSize = 9; // 512 bytes |
| nDataPageSize = 6; // 64 bytes |
| bDirty = 0; |
| memset( cReserved, 0, sizeof( cReserved ) ); |
| nFATSize = 0; |
| nTOCstrm = 0; |
| nReserved = 0; |
| nThreshold = 4096; |
| nDataFAT = 0; |
| nDataFATSize = 0; |
| nMasterChain = STG_EOF; |
| |
| SetTOCStart( STG_EOF ); |
| SetDataFATStart( STG_EOF ); |
| for( short i = 0; i < cFATPagesInHeader; i++ ) |
| SetFATPage( i, STG_FREE ); |
| } |
| |
| sal_Bool StgHeader::Load( StgIo& rIo ) |
| { |
| sal_Bool bResult = sal_False; |
| if ( rIo.GetStrm() ) |
| { |
| SvStream& r = *rIo.GetStrm(); |
| bResult = Load( r ); |
| bResult = ( bResult && rIo.Good() ); |
| } |
| |
| return bResult; |
| } |
| |
| sal_Bool StgHeader::Load( SvStream& r ) |
| { |
| r.Seek( 0L ); |
| r.Read( cSignature, 8 ); |
| r >> aClsId // 08 Class ID |
| >> nVersion // 1A version number |
| >> nByteOrder // 1C Unicode byte order indicator |
| >> nPageSize // 1E 1 << nPageSize = block size |
| >> nDataPageSize; // 20 1 << this size == data block size |
| r.SeekRel( 10 ); |
| r >> nFATSize // 2C total number of FAT pages |
| >> nTOCstrm // 30 starting page for the TOC stream |
| >> nReserved // 34 |
| >> nThreshold // 38 minimum file size for big data |
| >> nDataFAT // 3C page # of 1st data FAT block |
| >> nDataFATSize // 40 # of data FATpages |
| >> nMasterChain // 44 chain to the next master block |
| >> nMaster; // 48 # of additional master blocks |
| for( short i = 0; i < cFATPagesInHeader; i++ ) |
| r >> nMasterFAT[ i ]; |
| |
| return ( r.GetErrorCode() == ERRCODE_NONE && Check() ); |
| } |
| |
| sal_Bool StgHeader::Store( StgIo& rIo ) |
| { |
| if( !bDirty ) |
| return sal_True; |
| SvStream& r = *rIo.GetStrm(); |
| r.Seek( 0L ); |
| r.Write( cSignature, 8 + 16 ); |
| r << nVersion // 1A version number |
| << nByteOrder // 1C Unicode byte order indicator |
| << nPageSize // 1E 1 << nPageSize = block size |
| << nDataPageSize // 20 1 << this size == data block size |
| << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int16) 0 |
| << nFATSize // 2C total number of FAT pages |
| << nTOCstrm // 30 starting page for the TOC stream |
| << nReserved // 34 |
| << nThreshold // 38 minimum file size for big data |
| << nDataFAT // 3C page # of 1st data FAT block |
| << nDataFATSize // 40 # of data FAT pages |
| << nMasterChain // 44 chain to the next master block |
| << nMaster; // 48 # of additional master blocks |
| for( short i = 0; i < cFATPagesInHeader; i++ ) |
| r << nMasterFAT[ i ]; |
| bDirty = !rIo.Good(); |
| return sal_Bool( !bDirty ); |
| } |
| |
| static bool lcl_wontoverflow(short shift) |
| { |
| return shift >= 0 && shift < (short)sizeof(short) * 8 - 1; |
| } |
| |
| // Perform thorough checks also on unknown variables |
| sal_Bool StgHeader::Check() |
| { |
| return sal_Bool( memcmp( cSignature, cStgSignature, 8 ) == 0 |
| && (short) ( nVersion >> 16 ) == 3 ) |
| && nPageSize == 9 |
| && lcl_wontoverflow(nPageSize) |
| && lcl_wontoverflow(nDataPageSize) |
| && nFATSize > 0 |
| && nTOCstrm >= 0 |
| && nThreshold > 0 |
| && ( nDataFAT == STG_EOF || ( nDataFAT >= 0 && nDataFATSize > 0 ) ) |
| && ( nMasterChain == STG_FREE || nMasterChain == STG_EOF || ( nMasterChain >=0 && nMaster > 0 ) ) |
| && nMaster >= 0; |
| } |
| |
| sal_Int32 StgHeader::GetFATPage( short n ) const |
| { |
| if( n >= 0 && n < cFATPagesInHeader ) |
| return nMasterFAT[ n ]; |
| else |
| return STG_EOF; |
| } |
| |
| void StgHeader::SetFATPage( short n, sal_Int32 nb ) |
| { |
| if( n >= 0 && n < cFATPagesInHeader ) |
| { |
| if( nMasterFAT[ n ] != nb ) |
| bDirty = sal_True, nMasterFAT[ n ] = nb; |
| } |
| } |
| |
| void StgHeader::SetClassId( const ClsId& r ) |
| { |
| if( memcmp( &aClsId, &r, sizeof( ClsId ) ) ) |
| bDirty = sal_True, memcpy( &aClsId, &r, sizeof( ClsId ) ); |
| } |
| |
| void StgHeader::SetTOCStart( sal_Int32 n ) |
| { |
| if( n != nTOCstrm ) bDirty = sal_True, nTOCstrm = n; |
| } |
| |
| void StgHeader::SetDataFATStart( sal_Int32 n ) |
| { |
| if( n != nDataFAT ) bDirty = sal_True, nDataFAT = n; |
| } |
| |
| void StgHeader::SetDataFATSize( sal_Int32 n ) |
| { |
| if( n != nDataFATSize ) bDirty = sal_True, nDataFATSize = n; |
| } |
| |
| void StgHeader::SetFATSize( sal_Int32 n ) |
| { |
| if( n != nFATSize ) bDirty = sal_True, nFATSize = n; |
| } |
| |
| void StgHeader::SetFATChain( sal_Int32 n ) |
| { |
| if( n != nMasterChain ) |
| bDirty = sal_True, nMasterChain = n; |
| } |
| |
| void StgHeader::SetMasters( sal_Int32 n ) |
| { |
| if( n != nMaster ) bDirty = sal_True, nMaster = n; |
| } |
| |
| ///////////////////////////// class StgEntry ///////////////////////////// |
| |
| // This class is only a wrapper around teh dir entry structure |
| // which retrieves and sets data. |
| |
| // The name must be smaller than 32 chars. Conversion into Unicode |
| // is easy, since the 1st 256 characters of the Windows ANSI set |
| // equal the 1st 256 Unicode characters. |
| /* |
| void ToUnicode_Impl( String& rName ) |
| { |
| rName.Erase( 32 ); |
| rName.Convert( ::GetSystemCharSet(), CHARSET_ANSI ); |
| // brute force is OK |
| sal_uInt8* p = (sal_uInt8*) rName.GetCharStr(); |
| for( sal_uInt16 i = 0; i < rName.Len(); i++, p++ ) |
| { |
| // check each character and substitute blanks for illegal ones |
| sal_uInt8 cChar = *p; |
| if( cChar == '!' || cChar == ':' || cChar == '\\' || cChar == '/' ) |
| *p = ' '; |
| } |
| } |
| */ |
| /* |
| static void FromUnicode( String& rName ) |
| { |
| rName.Convert( CHARSET_ANSI, ::GetSystemCharSet() ); |
| } |
| */ |
| sal_Bool StgEntry::Init() |
| { |
| memset( nName, 0, sizeof( nName ) ); |
| nNameLen = 0; |
| cType = 0; |
| cFlags = 0; |
| nLeft = 0; |
| nRight = 0; |
| nChild = 0; |
| memset( &aClsId, 0, sizeof( aClsId ) ); |
| nFlags = 0; |
| nMtime[0] = 0; nMtime[1] = 0; |
| nAtime[0] = 0; nAtime[1] = 0; |
| nPage1 = 0; |
| nSize = 0; |
| nUnknown = 0; |
| |
| SetLeaf( STG_LEFT, STG_FREE ); |
| SetLeaf( STG_RIGHT, STG_FREE ); |
| SetLeaf( STG_CHILD, STG_FREE ); |
| SetLeaf( STG_DATA, STG_EOF ); |
| return sal_True; |
| } |
| |
| static String ToUpperUnicode( const String & rStr ) |
| { |
| // I don't know the locale, so en_US is hopefully fine |
| /* |
| com.sun.star.lang.Locale aLocale; |
| aLocale.Language = OUString::createFromAscii( "en" ); |
| aLocale.Country = OUString::createFromAscii( "US" ); |
| */ |
| static rtl::OUString aEN=rtl::OUString::createFromAscii( "en" ); |
| static rtl::OUString aUS=rtl::OUString::createFromAscii( "US" ); |
| static CharClass aCC( com::sun::star::lang::Locale( aEN, aUS, rtl::OUString() ) ); |
| return aCC.toUpper( rStr, 0, rStr.Len() ); |
| } |
| |
| |
| sal_Bool StgEntry::SetName( const String& rName ) |
| { |
| // I don't know the locale, so en_US is hopefully fine |
| aName = ToUpperUnicode( rName ); |
| aName.Erase( 31 ); |
| |
| int i; |
| for( i = 0; i < aName.Len() && i < 32; i++ ) |
| nName[ i ] = rName.GetChar( sal_uInt16( i )); |
| while( i < 32 ) |
| nName[ i++ ] = 0; |
| nNameLen = ( aName.Len() + 1 ) << 1; |
| return sal_True; |
| } |
| |
| sal_Int32 StgEntry::GetLeaf( StgEntryRef eRef ) const |
| { |
| sal_Int32 n = -1; |
| switch( eRef ) |
| { |
| case STG_LEFT: n = nLeft; break; |
| case STG_RIGHT: n = nRight; break; |
| case STG_CHILD: n = nChild; break; |
| case STG_DATA: n = nPage1; break; |
| } |
| return n; |
| } |
| |
| void StgEntry::SetLeaf( StgEntryRef eRef, sal_Int32 nPage ) |
| { |
| switch( eRef ) |
| { |
| case STG_LEFT: nLeft = nPage; break; |
| case STG_RIGHT: nRight = nPage; break; |
| case STG_CHILD: nChild = nPage; break; |
| case STG_DATA: nPage1 = nPage; break; |
| } |
| } |
| |
| const sal_Int32* StgEntry::GetTime( StgEntryTime eTime ) const |
| { |
| return( eTime == STG_MODIFIED ) ? nMtime : nAtime; |
| } |
| |
| void StgEntry::SetTime( StgEntryTime eTime, sal_Int32* pTime ) |
| { |
| if( eTime == STG_MODIFIED ) |
| nMtime[ 0 ] = *pTime++, nMtime[ 1 ] = *pTime; |
| else |
| nAtime[ 0 ] = *pTime++, nAtime[ 1 ] = *pTime; |
| } |
| |
| void StgEntry::SetClassId( const ClsId& r ) |
| { |
| memcpy( &aClsId, &r, sizeof( ClsId ) ); |
| } |
| |
| void StgEntry::GetName( String& rName ) const |
| { |
| sal_uInt16 n = nNameLen; |
| if( n ) |
| n = ( n >> 1 ) - 1; |
| rName = String( nName, n ); |
| } |
| |
| // Compare two entries. Do this case-insensitive. |
| |
| short StgEntry::Compare( const StgEntry& r ) const |
| { |
| /* |
| short nRes = r.nNameLen - nNameLen; |
| if( !nRes ) return strcmp( r.aName, aName ); |
| else return nRes; |
| */ |
| sal_Int32 nRes = r.nNameLen - nNameLen; |
| if( !nRes ) |
| nRes = r.aName.CompareTo( aName ); |
| return (short)nRes; |
| //return aName.CompareTo( r.aName ); |
| } |
| |
| // These load/store operations are a bit more complicated, |
| // since they have to copy their contents into a packed structure. |
| |
| sal_Bool StgEntry::Load( const void* pFrom, sal_uInt32 nBufSize ) |
| { |
| if ( nBufSize < 128 ) |
| return sal_False; |
| |
| SvMemoryStream r( (sal_Char*) pFrom, nBufSize, STREAM_READ ); |
| for( short i = 0; i < 32; i++ ) |
| r >> nName[ i ]; // 00 name as WCHAR |
| r >> nNameLen // 40 size of name in bytes including 00H |
| >> cType // 42 entry type |
| >> cFlags // 43 0 or 1 (tree balance?) |
| >> nLeft // 44 left node entry |
| >> nRight // 48 right node entry |
| >> nChild // 4C 1st child entry if storage |
| >> aClsId // 50 class ID (optional) |
| >> nFlags // 60 state flags(?) |
| >> nMtime[ 0 ] // 64 modification time |
| >> nMtime[ 1 ] // 64 modification time |
| >> nAtime[ 0 ] // 6C creation and access time |
| >> nAtime[ 1 ] // 6C creation and access time |
| >> nPage1 // 74 starting block (either direct or translated) |
| >> nSize // 78 file size |
| >> nUnknown; // 7C unknown |
| |
| sal_uInt16 n = nNameLen; |
| if( n ) |
| n = ( n >> 1 ) - 1; |
| if ( n > 31 || |
| (nSize < 0 && cType != STG_STORAGE) || |
| ( nPage1 < 0 && nPage1 != STG_FREE && nPage1 != STG_EOF ) ) |
| { |
| // the size makes no sence for the substorage |
| // TODO/LATER: actually the size should be an unsigned value, but in this case it would mean a stream of more than 2Gb |
| return sal_False; |
| } |
| |
| aName = String( nName, n ); |
| // I don't know the locale, so en_US is hopefully fine |
| aName = ToUpperUnicode( aName ); |
| aName.Erase( 31 ); |
| |
| return sal_True; |
| } |
| |
| void StgEntry::Store( void* pTo ) |
| { |
| SvMemoryStream r( (sal_Char *)pTo, 128, STREAM_WRITE ); |
| for( short i = 0; i < 32; i++ ) |
| r << nName[ i ]; // 00 name as WCHAR |
| r << nNameLen // 40 size of name in bytes including 00H |
| << cType // 42 entry type |
| << cFlags // 43 0 or 1 (tree balance?) |
| << nLeft // 44 left node entry |
| << nRight // 48 right node entry |
| << nChild // 4C 1st child entry if storage; |
| << aClsId // 50 class ID (optional) |
| << nFlags // 60 state flags(?) |
| << nMtime[ 0 ] // 64 modification time |
| << nMtime[ 1 ] // 64 modification time |
| << nAtime[ 0 ] // 6C creation and access time |
| << nAtime[ 1 ] // 6C creation and access time |
| << nPage1 // 74 starting block (either direct or translated) |
| << nSize // 78 file size |
| << nUnknown; // 7C unknown |
| } |
| |