1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21 #include <string.h>
22 #include <rtl/ustring.hxx>
23 #include <com/sun/star/lang/Locale.hpp>
24 #include <unotools/charclass.hxx>
25 #include <sot/stg.hxx>
26 #include "stgelem.hxx"
27 #include "stgio.hxx"
28
29 static const sal_uInt16 nMaxLegalStr = 31;
30
31 static const sal_uInt8 cStgSignature[ 8 ] = { 0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1 };
32
33 ////////////////////////////// struct ClsId
34
ReadClsId(SvStream & r,ClsId & rId)35 SvStream& ReadClsId( SvStream& r, ClsId& rId )
36 {
37 r.ReadUInt32( rId.Data1 )
38 .ReadUInt16( rId.Data2 )
39 .ReadUInt16( rId.Data3 )
40 .ReadUChar( rId.Data4[0] )
41 .ReadUChar( rId.Data4[1] )
42 .ReadUChar( rId.Data4[2] )
43 .ReadUChar( rId.Data4[3] )
44 .ReadUChar( rId.Data4[4] )
45 .ReadUChar( rId.Data4[5] )
46 .ReadUChar( rId.Data4[6] )
47 .ReadUChar( rId.Data4[7] );
48 return r;
49 }
50
WriteClsId(SvStream & r,const ClsId & rId)51 SvStream& WriteClsId( SvStream& r, const ClsId& rId )
52 {
53 return
54 r .WriteUInt32( rId.Data1 )
55 .WriteUInt16( rId.Data2 )
56 .WriteUInt16( rId.Data3 )
57 .WriteUChar( rId.Data4[0] )
58 .WriteUChar( rId.Data4[1] )
59 .WriteUChar( rId.Data4[2] )
60 .WriteUChar( rId.Data4[3] )
61 .WriteUChar( rId.Data4[4] )
62 .WriteUChar( rId.Data4[5] )
63 .WriteUChar( rId.Data4[6] )
64 .WriteUChar( rId.Data4[7] );
65 }
66
67 ///////////////////////////// class StgHeader
68
StgHeader()69 StgHeader::StgHeader()
70 : m_nVersion( 0 )
71 , m_nByteOrder( 0 )
72 , m_nPageSize( 0 )
73 , m_nDataPageSize( 0 )
74 , m_bDirty( sal_uInt8(false) )
75 , m_nFATSize( 0 )
76 , m_nTOCstrm( 0 )
77 , m_nReserved( 0 )
78 , m_nThreshold( 0 )
79 , m_nDataFAT( 0 )
80 , m_nDataFATSize( 0 )
81 , m_nMasterChain( 0 )
82 , m_nMaster( 0 )
83 {
84 }
85
Init()86 void StgHeader::Init()
87 {
88 memcpy( m_cSignature, cStgSignature, 8 );
89 memset( &m_aClsId, 0, sizeof( ClsId ) );
90 m_nVersion = 0x0003003B;
91 m_nByteOrder = 0xFFFE;
92 m_nPageSize = 9; // 512 bytes
93 m_nDataPageSize = 6; // 64 bytes
94 m_bDirty = sal_uInt8(false);
95 memset( m_cReserved, 0, sizeof( m_cReserved ) );
96 m_nFATSize = 0;
97 m_nTOCstrm = 0;
98 m_nReserved = 0;
99 m_nThreshold = 4096;
100 m_nDataFAT = 0;
101 m_nDataFATSize = 0;
102 m_nMasterChain = STG_EOF;
103
104 SetTOCStart( STG_EOF );
105 SetDataFATStart( STG_EOF );
106 for( short i = 0; i < cFATPagesInHeader; i++ )
107 SetFATPage( i, STG_FREE );
108 }
109
Load(StgIo & rIo)110 bool StgHeader::Load( StgIo& rIo )
111 {
112 bool bResult = false;
113 if ( rIo.GetStrm() )
114 {
115 SvStream& r = *rIo.GetStrm();
116 bResult = Load( r );
117 bResult = ( bResult && rIo.Good() );
118 }
119
120 return bResult;
121 }
122
Load(SvStream & r)123 bool StgHeader::Load( SvStream& r )
124 {
125 r.Seek( 0 );
126 r.ReadBytes( m_cSignature, 8 );
127 ReadClsId( r, m_aClsId ); // 08 Class ID
128 r.ReadInt32( m_nVersion ) // 1A version number
129 .ReadUInt16( m_nByteOrder ) // 1C Unicode byte order indicator
130 .ReadInt16( m_nPageSize ) // 1E 1 << nPageSize = block size
131 .ReadInt16( m_nDataPageSize ); // 20 1 << this size == data block size
132 if (!r.good())
133 return false;
134 if (!checkSeek(r, r.Tell() + 10))
135 return false;
136 r.ReadInt32( m_nFATSize ) // 2C total number of FAT pages
137 .ReadInt32( m_nTOCstrm ) // 30 starting page for the TOC stream
138 .ReadInt32( m_nReserved ) // 34
139 .ReadInt32( m_nThreshold ) // 38 minimum file size for big data
140 .ReadInt32( m_nDataFAT ) // 3C page # of 1st data FAT block
141 .ReadInt32( m_nDataFATSize ) // 40 # of data FATpages
142 .ReadInt32( m_nMasterChain ) // 44 chain to the next master block
143 .ReadInt32( m_nMaster ); // 48 # of additional master blocks
144 for(sal_Int32 & i : m_nMasterFAT)
145 r.ReadInt32( i );
146
147 return r.good() && Check();
148 }
149
Store(StgIo & rIo)150 bool StgHeader::Store( StgIo& rIo )
151 {
152 if( !m_bDirty )
153 return true;
154
155 SvStream& r = *rIo.GetStrm();
156 r.Seek( 0 );
157 r.WriteBytes( m_cSignature, 8 );
158 WriteClsId( r, m_aClsId ); // 08 Class ID
159 r.WriteInt32( m_nVersion ) // 1A version number
160 .WriteUInt16( m_nByteOrder ) // 1C Unicode byte order indicator
161 .WriteInt16( m_nPageSize ) // 1E 1 << nPageSize = block size
162 .WriteInt16( m_nDataPageSize ) // 20 1 << this size == data block size
163 .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt16( 0 )
164 .WriteInt32( m_nFATSize ) // 2C total number of FAT pages
165 .WriteInt32( m_nTOCstrm ) // 30 starting page for the TOC stream
166 .WriteInt32( m_nReserved ) // 34
167 .WriteInt32( m_nThreshold ) // 38 minimum file size for big data
168 .WriteInt32( m_nDataFAT ) // 3C page # of 1st data FAT block
169 .WriteInt32( m_nDataFATSize ) // 40 # of data FAT pages
170 .WriteInt32( m_nMasterChain ) // 44 chain to the next master block
171 .WriteInt32( m_nMaster ); // 48 # of additional master blocks
172 for(sal_Int32 i : m_nMasterFAT)
173 r.WriteInt32( i );
174 m_bDirty = sal_uInt8(!rIo.Good());
175 return !m_bDirty;
176 }
177
lcl_wontoverflow(short shift)178 static bool lcl_wontoverflow(short shift)
179 {
180 return shift >= 0 && shift < short(sizeof(short)) * 8 - 1;
181 }
182
isKnownSpecial(sal_Int32 nLocation)183 static bool isKnownSpecial(sal_Int32 nLocation)
184 {
185 return (nLocation == STG_FREE ||
186 nLocation == STG_EOF ||
187 nLocation == STG_FAT ||
188 nLocation == STG_MASTER);
189 }
190
191 // Perform thorough checks also on unknown variables
Check()192 bool StgHeader::Check()
193 {
194 return memcmp( m_cSignature, cStgSignature, 8 ) == 0
195 && static_cast<short>( m_nVersion >> 16 ) == 3
196 && m_nPageSize == 9
197 && lcl_wontoverflow(m_nPageSize)
198 && lcl_wontoverflow(m_nDataPageSize)
199 && m_nFATSize > 0
200 && m_nTOCstrm >= 0
201 && m_nThreshold > 0
202 && ( isKnownSpecial(m_nDataFAT) || ( m_nDataFAT >= 0 && m_nDataFATSize > 0 ) )
203 && ( isKnownSpecial(m_nMasterChain) || m_nMasterChain >=0 )
204 && m_nMaster >= 0;
205 }
206
GetFATPage(short n) const207 sal_Int32 StgHeader::GetFATPage( short n ) const
208 {
209 if( n >= 0 && n < cFATPagesInHeader )
210 return m_nMasterFAT[ n ];
211 else
212 return STG_EOF;
213 }
214
SetFATPage(short n,sal_Int32 nb)215 void StgHeader::SetFATPage( short n, sal_Int32 nb )
216 {
217 if( n >= 0 && n < cFATPagesInHeader )
218 {
219 if( m_nMasterFAT[ n ] != nb )
220 {
221 m_bDirty = sal_uInt8(true);
222 m_nMasterFAT[ n ] = nb;
223 }
224 }
225 }
226
SetTOCStart(sal_Int32 n)227 void StgHeader::SetTOCStart( sal_Int32 n )
228 {
229 if( n != m_nTOCstrm )
230 {
231 m_bDirty = sal_uInt8(true);
232 m_nTOCstrm = n;
233 }
234 }
235
SetDataFATStart(sal_Int32 n)236 void StgHeader::SetDataFATStart( sal_Int32 n )
237 {
238 if( n != m_nDataFAT )
239 {
240 m_bDirty = sal_uInt8(true);
241 m_nDataFAT = n;
242 }
243 }
244
SetDataFATSize(sal_Int32 n)245 void StgHeader::SetDataFATSize( sal_Int32 n )
246 {
247 if( n != m_nDataFATSize )
248 {
249 m_bDirty = sal_uInt8(true);
250 m_nDataFATSize = n;
251 }
252 }
253
SetFATSize(sal_Int32 n)254 void StgHeader::SetFATSize( sal_Int32 n )
255 {
256 if( n != m_nFATSize )
257 {
258 m_bDirty = sal_uInt8(true);
259 m_nFATSize = n;
260 }
261 }
262
SetFATChain(sal_Int32 n)263 void StgHeader::SetFATChain( sal_Int32 n )
264 {
265 if( n != m_nMasterChain )
266 {
267 m_bDirty = sal_uInt8(true);
268 m_nMasterChain = n;
269 }
270 }
271
SetMasters(sal_Int32 n)272 void StgHeader::SetMasters( sal_Int32 n )
273 {
274 if( n != m_nMaster )
275 {
276 m_bDirty = sal_uInt8(true);
277 m_nMaster = n;
278 }
279 }
280
281 ///////////////////////////// class StgEntry
282
Init()283 void StgEntry::Init()
284 {
285 memset( m_nName, 0, sizeof( m_nName ) );
286 m_nNameLen = 0;
287 m_cType = 0;
288 m_cFlags = 0;
289 m_nLeft = 0;
290 m_nRight = 0;
291 m_nChild = 0;
292 memset( &m_aClsId, 0, sizeof( m_aClsId ) );
293 m_nFlags = 0;
294 m_nMtime[0] = 0; m_nMtime[1] = 0;
295 m_nAtime[0] = 0; m_nAtime[1] = 0;
296 m_nPage1 = 0;
297 m_nSize = 0;
298 m_nUnknown = 0;
299
300 SetLeaf( STG_LEFT, STG_FREE );
301 SetLeaf( STG_RIGHT, STG_FREE );
302 SetLeaf( STG_CHILD, STG_FREE );
303 SetLeaf( STG_DATA, STG_EOF );
304 }
305
ToUpperUnicode(const OUString & rStr)306 static OUString ToUpperUnicode( const OUString & rStr )
307 {
308 // I don't know the locale, so en_US is hopefully fine
309 static CharClass aCC( LanguageTag( css::lang::Locale( "en", "US", "" )) );
310 return aCC.uppercase( rStr );
311 }
312
SetName(const OUString & rName)313 void StgEntry::SetName( const OUString& rName )
314 {
315 // I don't know the locale, so en_US is hopefully fine
316 m_aName = ToUpperUnicode( rName );
317 if(m_aName.getLength() > nMaxLegalStr)
318 {
319 m_aName = m_aName.copy(0, nMaxLegalStr);
320 }
321
322 sal_Int32 i;
323 for( i = 0; i < rName.getLength() && i <= nMaxLegalStr; i++ )
324 {
325 m_nName[ i ] = rName[ i ];
326 }
327 while (i <= nMaxLegalStr)
328 {
329 m_nName[ i++ ] = 0;
330 }
331 m_nNameLen = ( rName.getLength() + 1 ) << 1;
332 }
333
GetLeaf(StgEntryRef eRef) const334 sal_Int32 StgEntry::GetLeaf( StgEntryRef eRef ) const
335 {
336 sal_Int32 n = -1;
337 switch( eRef )
338 {
339 case STG_LEFT: n = m_nLeft; break;
340 case STG_RIGHT: n = m_nRight; break;
341 case STG_CHILD: n = m_nChild; break;
342 case STG_DATA: n = m_nPage1; break;
343 }
344 return n;
345 }
346
SetLeaf(StgEntryRef eRef,sal_Int32 nPage)347 void StgEntry::SetLeaf( StgEntryRef eRef, sal_Int32 nPage )
348 {
349 switch( eRef )
350 {
351 case STG_LEFT: m_nLeft = nPage; break;
352 case STG_RIGHT: m_nRight = nPage; break;
353 case STG_CHILD: m_nChild = nPage; break;
354 case STG_DATA: m_nPage1 = nPage; break;
355 }
356 }
357
SetClassId(const ClsId & r)358 void StgEntry::SetClassId( const ClsId& r )
359 {
360 memcpy( &m_aClsId, &r, sizeof( ClsId ) );
361 }
362
GetName(OUString & rName) const363 void StgEntry::GetName( OUString& rName ) const
364 {
365 sal_uInt16 n = m_nNameLen;
366 if( n )
367 n = ( n >> 1 ) - 1;
368 rName = OUString(m_nName, n);
369 }
370
371 // Compare two entries. Do this case-insensitive.
372
Compare(const StgEntry & r) const373 sal_Int32 StgEntry::Compare( const StgEntry& r ) const
374 {
375 if (r.m_nNameLen != m_nNameLen)
376 return r.m_nNameLen > m_nNameLen ? 1 : -1;
377 else
378 return r.m_aName.compareTo(m_aName);
379 }
380
381 // These load/store operations are a bit more complicated,
382 // since they have to copy their contents into a packed structure.
383
Load(const void * pFrom,sal_uInt32 nBufSize,sal_uInt64 nUnderlyingStreamSize)384 bool StgEntry::Load(const void* pFrom, sal_uInt32 nBufSize, sal_uInt64 nUnderlyingStreamSize)
385 {
386 if ( nBufSize < 128 )
387 return false;
388
389 SvMemoryStream r( const_cast<void *>(pFrom), nBufSize, StreamMode::READ );
390 for(sal_Unicode & i : m_nName)
391 r.ReadUtf16( i ); // 00 name as WCHAR
392 r.ReadUInt16( m_nNameLen ) // 40 size of name in bytes including 00H
393 .ReadUChar( m_cType ) // 42 entry type
394 .ReadUChar( m_cFlags ) // 43 0 or 1 (tree balance?)
395 .ReadInt32( m_nLeft ) // 44 left node entry
396 .ReadInt32( m_nRight ) // 48 right node entry
397 .ReadInt32( m_nChild ); // 4C 1st child entry if storage
398 ReadClsId( r, m_aClsId ); // 50 class ID (optional)
399 r.ReadInt32( m_nFlags ) // 60 state flags(?)
400 .ReadInt32( m_nMtime[ 0 ] ) // 64 modification time
401 .ReadInt32( m_nMtime[ 1 ] ) // 64 modification time
402 .ReadInt32( m_nAtime[ 0 ] ) // 6C creation and access time
403 .ReadInt32( m_nAtime[ 1 ] ) // 6C creation and access time
404 .ReadInt32( m_nPage1 ) // 74 starting block (either direct or translated)
405 .ReadInt32( m_nSize ) // 78 file size
406 .ReadInt32( m_nUnknown ); // 7C unknown
407
408 sal_uInt16 n = m_nNameLen;
409 if( n )
410 n = ( n >> 1 ) - 1;
411
412 if (n > nMaxLegalStr)
413 return false;
414
415 if (m_cType != STG_STORAGE)
416 {
417 if (m_nPage1 < 0 && !isKnownSpecial(m_nPage1))
418 {
419 //bad pageid
420 return false;
421 }
422 if (m_cType == STG_EMPTY)
423 {
424 /*
425 tdf#112399 opens fine in MSOffice 2013 despite a massive m_nSize field
426
427 Free (unused) directory entries are marked with Object Type 0x0
428 (unknown or unallocated). The entire directory entry must consist of
429 all zeroes except for the child, right sibling, and left sibling
430 pointers, which must be initialized to NOSTREAM (0xFFFFFFFF).
431 */
432 m_nSize = 0;
433 }
434 if (m_nSize < 0)
435 {
436 // the size makes no sense for the substorage
437 // TODO/LATER: actually the size should be an unsigned value, but
438 // in this case it would mean a stream of more than 2Gb
439 return false;
440 }
441 if (static_cast<sal_uInt64>(m_nSize) > nUnderlyingStreamSize)
442 {
443 // surely an entry cannot be larger than the underlying file
444 return false;
445 }
446
447 }
448
449 m_aName = OUString(m_nName , n);
450 // I don't know the locale, so en_US is hopefully fine
451 m_aName = ToUpperUnicode( m_aName );
452 if(m_aName.getLength() > nMaxLegalStr)
453 {
454 m_aName = m_aName.copy(0, nMaxLegalStr);
455 }
456
457 return true;
458 }
459
Store(void * pTo)460 void StgEntry::Store( void* pTo )
461 {
462 SvMemoryStream r( pTo, 128, StreamMode::WRITE );
463 for(sal_Unicode i : m_nName)
464 r.WriteUInt16( i ); // 00 name as WCHAR
465 r.WriteUInt16( m_nNameLen ) // 40 size of name in bytes including 00H
466 .WriteUChar( m_cType ) // 42 entry type
467 .WriteUChar( m_cFlags ) // 43 0 or 1 (tree balance?)
468 .WriteInt32( m_nLeft ) // 44 left node entry
469 .WriteInt32( m_nRight ) // 48 right node entry
470 .WriteInt32( m_nChild ); // 4C 1st child entry if storage;
471 WriteClsId( r, m_aClsId ); // 50 class ID (optional)
472 r.WriteInt32( m_nFlags ) // 60 state flags(?)
473 .WriteInt32( m_nMtime[ 0 ] ) // 64 modification time
474 .WriteInt32( m_nMtime[ 1 ] ) // 64 modification time
475 .WriteInt32( m_nAtime[ 0 ] ) // 6C creation and access time
476 .WriteInt32( m_nAtime[ 1 ] ) // 6C creation and access time
477 .WriteInt32( m_nPage1 ) // 74 starting block (either direct or translated)
478 .WriteInt32( m_nSize ) // 78 file size
479 .WriteInt32( m_nUnknown ); // 7C unknown
480 }
481
482 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
483