1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include <o3tl/safeint.hxx>
24 #include <rtl/ustring.hxx>
25 #include <com/sun/star/lang/Locale.hpp>
26 #include <unotools/charclass.hxx>
27 #include <sot/stg.hxx>
28 #include "stgelem.hxx"
31 const sal_uInt16 nMaxLegalStr
= 31;
33 const sal_uInt8 cStgSignature
[ 8 ] = { 0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1 };
35 ////////////////////////////// struct ClsId
37 SvStream
& ReadClsId( SvStream
& r
, ClsId
& rId
)
39 r
.ReadUInt32( rId
.Data1
)
40 .ReadUInt16( rId
.Data2
)
41 .ReadUInt16( rId
.Data3
)
42 .ReadUChar( rId
.Data4
[0] )
43 .ReadUChar( rId
.Data4
[1] )
44 .ReadUChar( rId
.Data4
[2] )
45 .ReadUChar( rId
.Data4
[3] )
46 .ReadUChar( rId
.Data4
[4] )
47 .ReadUChar( rId
.Data4
[5] )
48 .ReadUChar( rId
.Data4
[6] )
49 .ReadUChar( rId
.Data4
[7] );
53 SvStream
& WriteClsId( SvStream
& r
, const ClsId
& rId
)
56 r
.WriteUInt32( rId
.Data1
)
57 .WriteUInt16( rId
.Data2
)
58 .WriteUInt16( rId
.Data3
)
59 .WriteUChar( rId
.Data4
[0] )
60 .WriteUChar( rId
.Data4
[1] )
61 .WriteUChar( rId
.Data4
[2] )
62 .WriteUChar( rId
.Data4
[3] )
63 .WriteUChar( rId
.Data4
[4] )
64 .WriteUChar( rId
.Data4
[5] )
65 .WriteUChar( rId
.Data4
[6] )
66 .WriteUChar( rId
.Data4
[7] );
69 ///////////////////////////// class StgHeader
71 StgHeader::StgHeader()
75 , m_nDataPageSize( 0 )
76 , m_bDirty( sal_uInt8(false) )
88 void StgHeader::Init()
90 memcpy( m_cSignature
, cStgSignature
, 8 );
91 memset( &m_aClsId
, 0, sizeof( ClsId
) );
92 m_nVersion
= 0x0003003B;
93 m_nByteOrder
= 0xFFFE;
94 m_nPageSize
= 9; // 512 bytes
95 m_nDataPageSize
= 6; // 64 bytes
96 m_bDirty
= sal_uInt8(false);
97 memset( m_cReserved
, 0, sizeof( m_cReserved
) );
104 m_nMasterChain
= STG_EOF
;
106 SetTOCStart( STG_EOF
);
107 SetDataFATStart( STG_EOF
);
108 for( short i
= 0; i
< cFATPagesInHeader
; i
++ )
109 SetFATPage( i
, STG_FREE
);
112 bool StgHeader::Load( StgIo
& rIo
)
114 bool bResult
= false;
117 SvStream
& r
= *rIo
.GetStrm();
119 bResult
= ( bResult
&& rIo
.Good() );
125 bool StgHeader::Load( SvStream
& r
)
128 r
.ReadBytes( m_cSignature
, 8 );
129 ReadClsId( r
, m_aClsId
); // 08 Class ID
130 r
.ReadInt32( m_nVersion
) // 1A version number
131 .ReadUInt16( m_nByteOrder
) // 1C Unicode byte order indicator
132 .ReadInt16( m_nPageSize
) // 1E 1 << nPageSize = block size
133 .ReadInt16( m_nDataPageSize
); // 20 1 << this size == data block size
136 if (!checkSeek(r
, r
.Tell() + 10))
138 r
.ReadInt32( m_nFATSize
) // 2C total number of FAT pages
139 .ReadInt32( m_nTOCstrm
) // 30 starting page for the TOC stream
140 .ReadInt32( m_nReserved
) // 34
141 .ReadInt32( m_nThreshold
) // 38 minimum file size for big data
142 .ReadInt32( m_nDataFAT
) // 3C page # of 1st data FAT block
143 .ReadInt32( m_nDataFATSize
) // 40 # of data FATpages
144 .ReadInt32( m_nMasterChain
) // 44 chain to the next master block
145 .ReadInt32( m_nMaster
); // 48 # of additional master blocks
146 for(sal_Int32
& i
: m_nMasterFAT
)
149 return r
.good() && Check();
152 bool StgHeader::Store( StgIo
& rIo
)
157 SvStream
& r
= *rIo
.GetStrm();
159 r
.WriteBytes( m_cSignature
, 8 );
160 WriteClsId( r
, m_aClsId
); // 08 Class ID
161 r
.WriteInt32( m_nVersion
) // 1A version number
162 .WriteUInt16( m_nByteOrder
) // 1C Unicode byte order indicator
163 .WriteInt16( m_nPageSize
) // 1E 1 << nPageSize = block size
164 .WriteInt16( m_nDataPageSize
) // 20 1 << this size == data block size
165 .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt16( 0 )
166 .WriteInt32( m_nFATSize
) // 2C total number of FAT pages
167 .WriteInt32( m_nTOCstrm
) // 30 starting page for the TOC stream
168 .WriteInt32( m_nReserved
) // 34
169 .WriteInt32( m_nThreshold
) // 38 minimum file size for big data
170 .WriteInt32( m_nDataFAT
) // 3C page # of 1st data FAT block
171 .WriteInt32( m_nDataFATSize
) // 40 # of data FAT pages
172 .WriteInt32( m_nMasterChain
) // 44 chain to the next master block
173 .WriteInt32( m_nMaster
); // 48 # of additional master blocks
174 for(sal_Int32 i
: m_nMasterFAT
)
176 m_bDirty
= sal_uInt8(!rIo
.Good());
180 static bool lcl_wontoverflow(short shift
)
182 return shift
>= 0 && shift
< short(sizeof(short)) * 8 - 1;
185 static bool isKnownSpecial(sal_Int32 nLocation
)
187 return (nLocation
== STG_FREE
||
188 nLocation
== STG_EOF
||
189 nLocation
== STG_FAT
||
190 nLocation
== STG_MASTER
);
193 // Perform thorough checks also on unknown variables
194 bool StgHeader::Check()
196 return memcmp( m_cSignature
, cStgSignature
, 8 ) == 0
197 && static_cast<short>( m_nVersion
>> 16 ) == 3
199 && lcl_wontoverflow(m_nPageSize
)
200 && lcl_wontoverflow(m_nDataPageSize
)
204 && ( isKnownSpecial(m_nDataFAT
) || ( m_nDataFAT
>= 0 && m_nDataFATSize
> 0 ) )
205 && ( isKnownSpecial(m_nMasterChain
) || m_nMasterChain
>=0 )
209 sal_Int32
StgHeader::GetFATPage( short n
) const
211 if( n
>= 0 && n
< cFATPagesInHeader
)
212 return m_nMasterFAT
[ n
];
217 void StgHeader::SetFATPage( short n
, sal_Int32 nb
)
219 if( n
>= 0 && n
< cFATPagesInHeader
)
221 if( m_nMasterFAT
[ n
] != nb
)
223 m_bDirty
= sal_uInt8(true);
224 m_nMasterFAT
[ n
] = nb
;
229 void StgHeader::SetTOCStart( sal_Int32 n
)
231 if( n
!= m_nTOCstrm
)
233 m_bDirty
= sal_uInt8(true);
238 void StgHeader::SetDataFATStart( sal_Int32 n
)
240 if( n
!= m_nDataFAT
)
242 m_bDirty
= sal_uInt8(true);
247 void StgHeader::SetDataFATSize( sal_Int32 n
)
249 if( n
!= m_nDataFATSize
)
251 m_bDirty
= sal_uInt8(true);
256 void StgHeader::SetFATSize( sal_Int32 n
)
258 if( n
!= m_nFATSize
)
260 m_bDirty
= sal_uInt8(true);
265 void StgHeader::SetFATChain( sal_Int32 n
)
267 if( n
!= m_nMasterChain
)
269 m_bDirty
= sal_uInt8(true);
274 void StgHeader::SetMasters( sal_Int32 n
)
278 m_bDirty
= sal_uInt8(true);
283 ///////////////////////////// class StgEntry
285 void StgEntry::Init()
287 memset( m_nName
, 0, sizeof( m_nName
) );
294 memset( &m_aClsId
, 0, sizeof( m_aClsId
) );
296 m_nMtime
[0] = 0; m_nMtime
[1] = 0;
297 m_nAtime
[0] = 0; m_nAtime
[1] = 0;
302 SetLeaf( STG_LEFT
, STG_FREE
);
303 SetLeaf( STG_RIGHT
, STG_FREE
);
304 SetLeaf( STG_CHILD
, STG_FREE
);
305 SetLeaf( STG_DATA
, STG_EOF
);
308 static OUString
ToUpperUnicode( const OUString
& rStr
)
310 // I don't know the locale, so en_US is hopefully fine
311 static CharClass
aCC( LanguageTag( css::lang::Locale( u
"en"_ustr
, u
"US"_ustr
, u
""_ustr
)) );
312 return aCC
.uppercase( rStr
);
315 void StgEntry::SetName( const OUString
& rName
)
317 // I don't know the locale, so en_US is hopefully fine
318 m_aName
= ToUpperUnicode( rName
);
319 if(m_aName
.getLength() > nMaxLegalStr
)
321 m_aName
= m_aName
.copy(0, nMaxLegalStr
);
325 for( i
= 0; i
< rName
.getLength() && i
<= nMaxLegalStr
; i
++ )
327 m_nName
[ i
] = rName
[ i
];
329 while (i
<= nMaxLegalStr
)
333 m_nNameLen
= ( rName
.getLength() + 1 ) << 1;
336 sal_Int32
StgEntry::GetLeaf( StgEntryRef eRef
) const
341 case STG_LEFT
: n
= m_nLeft
; break;
342 case STG_RIGHT
: n
= m_nRight
; break;
343 case STG_CHILD
: n
= m_nChild
; break;
344 case STG_DATA
: n
= m_nPage1
; break;
349 void StgEntry::SetLeaf( StgEntryRef eRef
, sal_Int32 nPage
)
353 case STG_LEFT
: m_nLeft
= nPage
; break;
354 case STG_RIGHT
: m_nRight
= nPage
; break;
355 case STG_CHILD
: m_nChild
= nPage
; break;
356 case STG_DATA
: m_nPage1
= nPage
; break;
360 void StgEntry::SetClassId( const ClsId
& r
)
362 memcpy( &m_aClsId
, &r
, sizeof( ClsId
) );
365 void StgEntry::GetName( OUString
& rName
) const
367 sal_uInt16 n
= m_nNameLen
;
370 rName
= OUString(m_nName
, n
);
373 // Compare two entries. Do this case-insensitive.
375 sal_Int32
StgEntry::Compare( const StgEntry
& r
) const
377 if (r
.m_nNameLen
!= m_nNameLen
)
378 return r
.m_nNameLen
> m_nNameLen
? 1 : -1;
380 return r
.m_aName
.compareTo(m_aName
);
383 // These load/store operations are a bit more complicated,
384 // since they have to copy their contents into a packed structure.
386 bool StgEntry::Load(const void* pFrom
, sal_uInt32 nBufSize
, sal_uInt64 nUnderlyingStreamSize
)
388 if ( nBufSize
< 128 )
391 SvMemoryStream
r( const_cast<void *>(pFrom
), nBufSize
, StreamMode::READ
);
392 for(sal_Unicode
& i
: m_nName
)
393 r
.ReadUtf16( i
); // 00 name as WCHAR
394 r
.ReadUInt16( m_nNameLen
) // 40 size of name in bytes including 00H
395 .ReadUChar( m_cType
) // 42 entry type
396 .ReadUChar( m_cFlags
) // 43 0 or 1 (tree balance?)
397 .ReadInt32( m_nLeft
) // 44 left node entry
398 .ReadInt32( m_nRight
) // 48 right node entry
399 .ReadInt32( m_nChild
); // 4C 1st child entry if storage
400 ReadClsId( r
, m_aClsId
); // 50 class ID (optional)
401 r
.ReadInt32( m_nFlags
) // 60 state flags(?)
402 .ReadInt32( m_nMtime
[ 0 ] ) // 64 modification time
403 .ReadInt32( m_nMtime
[ 1 ] ) // 64 modification time
404 .ReadInt32( m_nAtime
[ 0 ] ) // 6C creation and access time
405 .ReadInt32( m_nAtime
[ 1 ] ) // 6C creation and access time
406 .ReadInt32( m_nPage1
) // 74 starting block (either direct or translated)
407 .ReadInt32( m_nSize
) // 78 file size
408 .ReadInt32( m_nUnknown
); // 7C unknown
410 sal_uInt16 n
= m_nNameLen
;
414 if (n
> nMaxLegalStr
)
417 if (m_cType
!= STG_STORAGE
)
419 if (m_nPage1
< 0 && !isKnownSpecial(m_nPage1
))
424 if (m_cType
== STG_EMPTY
)
427 tdf#112399 opens fine in MSOffice 2013 despite a massive m_nSize field
429 Free (unused) directory entries are marked with Object Type 0x0
430 (unknown or unallocated). The entire directory entry must consist of
431 all zeroes except for the child, right sibling, and left sibling
432 pointers, which must be initialized to NOSTREAM (0xFFFFFFFF).
438 // the size makes no sense for the substorage
439 // TODO/LATER: actually the size should be an unsigned value, but
440 // in this case it would mean a stream of more than 2Gb
443 if (o3tl::make_unsigned(m_nSize
) > nUnderlyingStreamSize
)
445 // surely an entry cannot be larger than the underlying file
451 m_aName
= OUString(m_nName
, n
);
452 // I don't know the locale, so en_US is hopefully fine
453 m_aName
= ToUpperUnicode( m_aName
);
454 if(m_aName
.getLength() > nMaxLegalStr
)
456 m_aName
= m_aName
.copy(0, nMaxLegalStr
);
462 void StgEntry::Store( void* pTo
)
464 SvMemoryStream
r( pTo
, 128, StreamMode::WRITE
);
465 for(sal_Unicode i
: m_nName
)
466 r
.WriteUInt16( i
); // 00 name as WCHAR
467 r
.WriteUInt16( m_nNameLen
) // 40 size of name in bytes including 00H
468 .WriteUChar( m_cType
) // 42 entry type
469 .WriteUChar( m_cFlags
) // 43 0 or 1 (tree balance?)
470 .WriteInt32( m_nLeft
) // 44 left node entry
471 .WriteInt32( m_nRight
) // 48 right node entry
472 .WriteInt32( m_nChild
); // 4C 1st child entry if storage;
473 WriteClsId( r
, m_aClsId
); // 50 class ID (optional)
474 r
.WriteInt32( m_nFlags
) // 60 state flags(?)
475 .WriteInt32( m_nMtime
[ 0 ] ) // 64 modification time
476 .WriteInt32( m_nMtime
[ 1 ] ) // 64 modification time
477 .WriteInt32( m_nAtime
[ 0 ] ) // 6C creation and access time
478 .WriteInt32( m_nAtime
[ 1 ] ) // 6C creation and access time
479 .WriteInt32( m_nPage1
) // 74 starting block (either direct or translated)
480 .WriteInt32( m_nSize
) // 78 file size
481 .WriteInt32( m_nUnknown
); // 7C unknown
484 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */