Related: tdf#163126 fix failure to restart print job
[LibreOffice.git] / sot / source / sdstor / stgelem.cxx
bloba4d06eb55d5a51aabedeec0db61ec915dfee467f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <string.h>
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"
29 #include "stgio.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] );
50 return r;
53 SvStream& WriteClsId( SvStream& r, const ClsId& rId )
55 return
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()
72 : m_nVersion( 0 )
73 , m_nByteOrder( 0 )
74 , m_nPageSize( 0 )
75 , m_nDataPageSize( 0 )
76 , m_bDirty( sal_uInt8(false) )
77 , m_nFATSize( 0 )
78 , m_nTOCstrm( 0 )
79 , m_nReserved( 0 )
80 , m_nThreshold( 0 )
81 , m_nDataFAT( 0 )
82 , m_nDataFATSize( 0 )
83 , m_nMasterChain( 0 )
84 , m_nMaster( 0 )
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 ) );
98 m_nFATSize = 0;
99 m_nTOCstrm = 0;
100 m_nReserved = 0;
101 m_nThreshold = 4096;
102 m_nDataFAT = 0;
103 m_nDataFATSize = 0;
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;
115 if ( rIo.GetStrm() )
117 SvStream& r = *rIo.GetStrm();
118 bResult = Load( r );
119 bResult = ( bResult && rIo.Good() );
122 return bResult;
125 bool StgHeader::Load( SvStream& r )
127 r.Seek( 0 );
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
134 if (!r.good())
135 return false;
136 if (!checkSeek(r, r.Tell() + 10))
137 return false;
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)
147 r.ReadInt32( i );
149 return r.good() && Check();
152 bool StgHeader::Store( StgIo& rIo )
154 if( !m_bDirty )
155 return true;
157 SvStream& r = *rIo.GetStrm();
158 r.Seek( 0 );
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)
175 r.WriteInt32( i );
176 m_bDirty = sal_uInt8(!rIo.Good());
177 return !m_bDirty;
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
198 && m_nPageSize == 9
199 && lcl_wontoverflow(m_nPageSize)
200 && lcl_wontoverflow(m_nDataPageSize)
201 && m_nFATSize > 0
202 && m_nTOCstrm >= 0
203 && m_nThreshold > 0
204 && ( isKnownSpecial(m_nDataFAT) || ( m_nDataFAT >= 0 && m_nDataFATSize > 0 ) )
205 && ( isKnownSpecial(m_nMasterChain) || m_nMasterChain >=0 )
206 && m_nMaster >= 0;
209 sal_Int32 StgHeader::GetFATPage( short n ) const
211 if( n >= 0 && n < cFATPagesInHeader )
212 return m_nMasterFAT[ n ];
213 else
214 return STG_EOF;
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);
234 m_nTOCstrm = n;
238 void StgHeader::SetDataFATStart( sal_Int32 n )
240 if( n != m_nDataFAT )
242 m_bDirty = sal_uInt8(true);
243 m_nDataFAT = n;
247 void StgHeader::SetDataFATSize( sal_Int32 n )
249 if( n != m_nDataFATSize )
251 m_bDirty = sal_uInt8(true);
252 m_nDataFATSize = n;
256 void StgHeader::SetFATSize( sal_Int32 n )
258 if( n != m_nFATSize )
260 m_bDirty = sal_uInt8(true);
261 m_nFATSize = n;
265 void StgHeader::SetFATChain( sal_Int32 n )
267 if( n != m_nMasterChain )
269 m_bDirty = sal_uInt8(true);
270 m_nMasterChain = n;
274 void StgHeader::SetMasters( sal_Int32 n )
276 if( n != m_nMaster )
278 m_bDirty = sal_uInt8(true);
279 m_nMaster = n;
283 ///////////////////////////// class StgEntry
285 void StgEntry::Init()
287 memset( m_nName, 0, sizeof( m_nName ) );
288 m_nNameLen = 0;
289 m_cType = 0;
290 m_cFlags = 0;
291 m_nLeft = 0;
292 m_nRight = 0;
293 m_nChild = 0;
294 memset( &m_aClsId, 0, sizeof( m_aClsId ) );
295 m_nFlags = 0;
296 m_nMtime[0] = 0; m_nMtime[1] = 0;
297 m_nAtime[0] = 0; m_nAtime[1] = 0;
298 m_nPage1 = 0;
299 m_nSize = 0;
300 m_nUnknown = 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);
324 sal_Int32 i;
325 for( i = 0; i < rName.getLength() && i <= nMaxLegalStr; i++ )
327 m_nName[ i ] = rName[ i ];
329 while (i <= nMaxLegalStr)
331 m_nName[ i++ ] = 0;
333 m_nNameLen = ( rName.getLength() + 1 ) << 1;
336 sal_Int32 StgEntry::GetLeaf( StgEntryRef eRef ) const
338 sal_Int32 n = -1;
339 switch( eRef )
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;
346 return n;
349 void StgEntry::SetLeaf( StgEntryRef eRef, sal_Int32 nPage )
351 switch( eRef )
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;
368 if( n )
369 n = ( n >> 1 ) - 1;
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;
379 else
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 )
389 return false;
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;
411 if( n )
412 n = ( n >> 1 ) - 1;
414 if (n > nMaxLegalStr)
415 return false;
417 if (m_cType != STG_STORAGE)
419 if (m_nPage1 < 0 && !isKnownSpecial(m_nPage1))
421 //bad pageid
422 return false;
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).
434 m_nSize = 0;
436 if (m_nSize < 0)
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
441 return false;
443 if (o3tl::make_unsigned(m_nSize) > nUnderlyingStreamSize)
445 // surely an entry cannot be larger than the underlying file
446 return false;
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);
459 return true;
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: */