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