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 .
20 #include "propread.hxx"
21 #include <rtl/tencinfo.h>
22 #include <rtl/textenc.h>
23 #include <sal/log.hxx>
24 #include <o3tl/sorted_vector.hxx>
25 #include <osl/diagnose.h>
28 PropEntry::PropEntry( sal_uInt32 nId
, const sal_uInt8
* pBuf
, sal_uInt32 nBufSize
) :
31 mpBuf ( new sal_uInt8
[ nBufSize
] )
33 memcpy( mpBuf
.get(), pBuf
, nBufSize
);
36 PropEntry::PropEntry( const PropEntry
& rProp
) :
38 mnSize ( rProp
.mnSize
),
39 mpBuf ( new sal_uInt8
[ mnSize
] )
41 memcpy( mpBuf
.get(), rProp
.mpBuf
.get(), mnSize
);
44 PropEntry
& PropEntry::operator=(const PropEntry
& rPropEntry
)
46 if ( this != &rPropEntry
)
48 mnId
= rPropEntry
.mnId
;
49 mnSize
= rPropEntry
.mnSize
;
50 mpBuf
.reset( new sal_uInt8
[ mnSize
] );
51 memcpy( mpBuf
.get(), rPropEntry
.mpBuf
.get(), mnSize
);
56 void PropItem::Clear()
58 Seek( STREAM_SEEK_TO_BEGIN
);
59 delete[] static_cast<sal_uInt8
*>(SwitchBuffer());
62 static sal_Int32
lcl_getMaxSafeStrLen(sal_uInt32 nSize
)
64 nSize
-= 1; //Drop NULL terminator
66 //If it won't fit in a string, clip it to the max size that does
67 if (nSize
> SAL_MAX_INT32
)
68 nSize
= SAL_MAX_INT32
;
70 return static_cast< sal_Int32
>( nSize
);
73 bool PropItem::Read( OUString
& rString
, sal_uInt32 nStringType
, bool bAlign
)
75 sal_uInt32 nType
, nItemPos
;
76 bool bRetValue
= false;
80 if ( nStringType
== VT_EMPTY
)
82 nType
= VT_NULL
; // Initialize in case stream fails.
86 nType
= nStringType
& VT_TYPEMASK
;
88 sal_uInt32
nItemSize(0); // Initialize in case stream fails.
89 ReadUInt32(nItemSize
);
97 auto nMaxSizePossible
= remainingSize();
98 if (nItemSize
> nMaxSizePossible
)
100 SAL_WARN("sd.filter", "String of Len " << nItemSize
<< " claimed, only " << nMaxSizePossible
<< " possible");
101 nItemSize
= nMaxSizePossible
;
109 std::unique_ptr
<char[]> pString( new char[ nItemSize
] );
110 if ( mnTextEnc
== RTL_TEXTENCODING_UCS2
)
115 sal_Unicode
* pWString
= reinterpret_cast<sal_Unicode
*>(pString
.get());
116 for (sal_uInt32 i
= 0; i
< nItemSize
; ++i
)
117 ReadUtf16( pWString
[ i
] );
118 rString
= OUString(pWString
, lcl_getMaxSafeStrLen(nItemSize
));
126 SvMemoryStream::ReadBytes(pString
.get(), nItemSize
);
127 if ( pString
[ nItemSize
- 1 ] == 0 )
130 rString
= OUString(pString
.get(), rtl_str_getLength(pString
.get()), mnTextEnc
);
137 catch( const std::bad_alloc
& )
139 OSL_FAIL( "sd PropItem::Read bad alloc" );
143 SeekRel( ( 4 - ( nItemSize
& 3 ) ) & 3 ); // dword align
151 auto nMaxSizePossible
= remainingSize() / sizeof(sal_Unicode
);
152 if (nItemSize
> nMaxSizePossible
)
154 SAL_WARN("sd.filter", "String of Len " << nItemSize
<< " claimed, only " << nMaxSizePossible
<< " possible");
155 nItemSize
= nMaxSizePossible
;
163 std::unique_ptr
<sal_Unicode
[]> pString( new sal_Unicode
[ nItemSize
] );
164 for (sal_uInt32 i
= 0; i
< nItemSize
; ++i
)
165 ReadUtf16( pString
[ i
] );
166 if ( pString
[ nItemSize
- 1 ] == 0 )
168 if ( static_cast<sal_uInt16
>(nItemSize
) > 1 )
169 rString
= OUString(pString
.get(), lcl_getMaxSafeStrLen(nItemSize
));
175 catch( const std::bad_alloc
& )
177 OSL_FAIL( "sd PropItem::Read bad alloc" );
180 if ( bAlign
&& ( nItemSize
& 1 ) )
181 SeekRel( 2 ); // dword align
190 PropItem
& PropItem::operator=( PropItem
& rPropItem
)
192 if ( this != &rPropItem
)
194 Seek( STREAM_SEEK_TO_BEGIN
);
195 delete[] static_cast<sal_uInt8
*>(SwitchBuffer());
197 mnTextEnc
= rPropItem
.mnTextEnc
;
198 SvMemoryStream::WriteBytes(rPropItem
.GetData(), rPropItem
.TellEnd());
203 Section::Section( const Section
& rSection
)
204 : mnTextEnc(rSection
.mnTextEnc
)
206 for ( int i
= 0; i
< 16; i
++ )
207 aFMTID
[ i
] = rSection
.aFMTID
[ i
];
208 for(const std::unique_ptr
<PropEntry
>& rEntry
: rSection
.maEntries
)
209 maEntries
.push_back(std::make_unique
<PropEntry
>(*rEntry
));
212 Section::Section( const sal_uInt8
* pFMTID
) : mnTextEnc(RTL_TEXTENCODING_MS_1252
)
214 for ( int i
= 0; i
< 16; i
++ )
215 aFMTID
[ i
] = pFMTID
[ i
];
218 bool Section::GetProperty( sal_uInt32 nId
, PropItem
& rPropItem
)
222 auto iter
= std::find_if(maEntries
.begin(), maEntries
.end(),
223 [nId
](const std::unique_ptr
<PropEntry
>& rxEntry
) { return rxEntry
->mnId
== nId
; });
225 if (iter
!= maEntries
.end())
228 rPropItem
.SetTextEncoding( mnTextEnc
);
229 rPropItem
.WriteBytes( (*iter
)->mpBuf
.get(), (*iter
)->mnSize
);
230 rPropItem
.Seek( STREAM_SEEK_TO_BEGIN
);
237 void Section::AddProperty( sal_uInt32 nId
, const sal_uInt8
* pBuf
, sal_uInt32 nBufSize
)
239 // just a simple id check
243 if ( nId
== 0xffffffff )
246 // do not allow same PropId's, sort
247 auto iter
= std::find_if(maEntries
.begin(), maEntries
.end(),
248 [nId
](const std::unique_ptr
<PropEntry
>& rxEntry
) { return rxEntry
->mnId
>= nId
; });
249 if (iter
!= maEntries
.end())
251 if ( (*iter
)->mnId
== nId
)
252 (*iter
).reset(new PropEntry( nId
, pBuf
, nBufSize
));
254 maEntries
.insert( iter
, std::make_unique
<PropEntry
>( nId
, pBuf
, nBufSize
));
258 maEntries
.push_back( std::make_unique
<PropEntry
>( nId
, pBuf
, nBufSize
) );
262 void Section::GetDictionary(PropDictionary
& rDict
)
264 auto iter
= std::find_if(maEntries
.begin(), maEntries
.end(),
265 [](const std::unique_ptr
<PropEntry
>& rxEntry
) { return rxEntry
->mnId
== 0; });
267 if (iter
== maEntries
.end())
270 SvMemoryStream
aStream( (*iter
)->mpBuf
.get(), (*iter
)->mnSize
, StreamMode::READ
);
271 aStream
.Seek( STREAM_SEEK_TO_BEGIN
);
272 sal_uInt32
nDictCount(0);
273 aStream
.ReadUInt32( nDictCount
);
274 for (sal_uInt32 i
= 0; i
< nDictCount
; ++i
)
276 sal_uInt32
nId(0), nSize(0);
277 aStream
.ReadUInt32(nId
).ReadUInt32(nSize
);
278 if (!aStream
.good() || nSize
> aStream
.remainingSize())
280 if (mnTextEnc
== RTL_TEXTENCODING_UCS2
)
287 if ( mnTextEnc
== RTL_TEXTENCODING_UCS2
)
289 std::unique_ptr
<sal_Unicode
[]> pWString( new sal_Unicode
[nSize
] );
290 for (sal_uInt32 j
= 0; j
< nSize
; ++j
)
291 aStream
.ReadUtf16(pWString
[j
]);
292 aString
= OUString(pWString
.get(), lcl_getMaxSafeStrLen(nSize
));
296 std::unique_ptr
<char[]> pString( new char[nSize
] );
297 aStream
.ReadBytes(pString
.get(), nSize
);
298 aString
= OUString(pString
.get(), lcl_getMaxSafeStrLen(nSize
), mnTextEnc
);
301 catch( const std::bad_alloc
& )
303 OSL_FAIL( "sd Section::GetDictionary bad alloc" );
305 if (aString
.isEmpty())
307 rDict
.insert( std::make_pair(aString
,nId
) );
311 void Section::Read( SotStorageStream
*pStrm
)
313 sal_uInt64 nSecOfs
= pStrm
->Tell();
314 sal_uInt64 nStrmSize
= pStrm
->remainingSize();
316 mnTextEnc
= RTL_TEXTENCODING_MS_1252
;
317 sal_uInt32
nSecSize(0), nPropCount(0);
318 pStrm
->ReadUInt32(nSecSize
).ReadUInt32(nPropCount
);
319 if (nSecSize
> nStrmSize
)
321 SAL_WARN("sd.filter", "Section Len " << nSecSize
<< " claimed, only " << nStrmSize
<< " possible");
322 nSecSize
= nStrmSize
;
325 while (nPropCount
> 0)
328 sal_uInt32
nPropId(0), nPropOfs(0);
329 pStrm
->ReadUInt32(nPropId
).ReadUInt32(nPropOfs
);
332 auto nCurrent
= pStrm
->Tell();
333 sal_uInt64 nOffset
= nPropOfs
+ nSecOfs
;
334 if (!checkSeek(*pStrm
, nOffset
))
336 if ( nPropId
) // do not read dictionary
338 sal_uInt32
nPropType(0), nVectorCount(0);
339 pStrm
->ReadUInt32(nPropType
);
341 sal_uInt32 nPropSize
= 4;
342 if ( nPropType
& VT_VECTOR
)
344 pStrm
->ReadUInt32( nVectorCount
);
345 nPropType
&=~VT_VECTOR
;
351 bool bVariant
= ( nPropType
== VT_VARIANT
);
353 o3tl::sorted_vector
<sal_uInt64
> aVisitedOffsets
;
355 for (sal_uInt32 i
= 0; nPropSize
&& i
< nVectorCount
&& pStrm
->good(); ++i
)
359 pStrm
->ReadUInt32( nPropType
);
392 pStrm
->ReadUInt32( nTemp
);
393 nPropSize
+= ( nTemp
+ 4 );
397 pStrm
->ReadUInt32( nTemp
);
398 nPropSize
+= ( nTemp
+ 4 );
403 pStrm
->ReadUInt32( nTemp
);
404 // looks like these are aligned to 4 bytes
405 sal_uInt64 nLength
= nPropOfs
+ nSecOfs
+ nPropSize
+ ( nTemp
<< 1 ) + 4;
406 nPropSize
+= ( nTemp
<< 1 ) + 4 + (nLength
% 4);
410 case VT_BLOB_OBJECT
:
413 pStrm
->ReadUInt32( nTemp
);
414 nPropSize
+= ( nTemp
+ 4 );
420 case VT_STREAMED_OBJECT
:
421 case VT_STORED_OBJECT
:
429 if ( ( nVectorCount
- i
) > 1 )
431 nOffset
= nPropOfs
+ nSecOfs
+ nPropSize
;
432 if (!checkSeek(*pStrm
, nOffset
))
434 // inserts returns false if an equivalent element already existed
435 if (!aVisitedOffsets
.insert(nOffset
).second
)
437 SAL_WARN("sd.filter", "loop in Section::Read property list");
447 if ( nPropSize
> nStrmSize
)
451 pStrm
->Seek( nPropOfs
+ nSecOfs
);
452 // make sure we don't overflow the section size
453 if( nPropSize
> nSecSize
- nSecOfs
)
454 nPropSize
= nSecSize
- nSecOfs
;
455 std::unique_ptr
<sal_uInt8
[]> pBuf( new sal_uInt8
[ nPropSize
] );
456 nPropSize
= pStrm
->ReadBytes(pBuf
.get(), nPropSize
);
457 AddProperty( nPropId
, pBuf
.get(), nPropSize
);
462 if ( GetProperty( 1, aPropItem
) )
464 aPropItem
.ReadUInt32( nPropType
);
465 if ( nPropType
== VT_I2
)
467 sal_uInt16
nCodePage(0);
468 aPropItem
.ReadUInt16(nCodePage
);
470 if ( nCodePage
== 1200 )
472 mnTextEnc
= RTL_TEXTENCODING_UCS2
;
476 mnTextEnc
= rtl_getTextEncodingFromWindowsCodePage( nCodePage
);
477 if ( mnTextEnc
== RTL_TEXTENCODING_DONTKNOW
)
478 mnTextEnc
= RTL_TEXTENCODING_MS_1252
;
483 mnTextEnc
= RTL_TEXTENCODING_MS_1252
;
490 sal_uInt32
nDictCount(0);
491 pStrm
->ReadUInt32(nDictCount
);
492 auto nMaxRecordsPossible
= pStrm
->remainingSize() / (sizeof(sal_uInt32
)*2);
493 if (nDictCount
> nMaxRecordsPossible
)
495 SAL_WARN("sd.filter", "Dictionary count of " << nDictCount
<< " claimed, only " << nMaxRecordsPossible
<< " possible");
496 nDictCount
= nMaxRecordsPossible
;
498 for (sal_uInt32 i
= 0; i
< nDictCount
; ++i
)
501 pStrm
->ReadUInt32( nSize
).ReadUInt32( nSize
);
504 sal_uInt64 nPos
= pStrm
->Tell() + nSize
;
505 if (!checkSeek(*pStrm
, nPos
))
508 sal_uInt64 nSize
= pStrm
->Tell();
509 pStrm
->Seek( nPropOfs
+ nSecOfs
);
510 nSize
-= pStrm
->Tell();
511 if ( nSize
> nStrmSize
)
515 std::unique_ptr
<sal_uInt8
[]> pBuf( new sal_uInt8
[ nSize
] );
516 nSize
= pStrm
->ReadBytes(pBuf
.get(), nSize
);
517 AddProperty( 0xffffffff, pBuf
.get(), nSize
);
519 pStrm
->Seek(nCurrent
);
521 pStrm
->Seek(nSecOfs
+ nSecSize
);
524 Section
& Section::operator=( const Section
& rSection
)
526 if ( this != &rSection
)
528 memcpy( static_cast<void*>(aFMTID
), static_cast<void const *>(rSection
.aFMTID
), 16 );
530 for(const std::unique_ptr
<PropEntry
>& rEntry
: rSection
.maEntries
)
531 maEntries
.push_back(std::make_unique
<PropEntry
>(*rEntry
));
536 PropRead::PropRead( SotStorage
& rStorage
, const OUString
& rName
) :
538 mnByteOrder ( 0xfffe )
540 if ( rStorage
.IsStream( rName
) )
542 mpSvStream
= rStorage
.OpenSotStream( rName
, StreamMode::STD_READ
);
543 if ( mpSvStream
.is() )
545 mpSvStream
->SetEndian( SvStreamEndian::LITTLE
);
546 memset( mApplicationCLSID
, 0, 16 );
552 const Section
* PropRead::GetSection( const sal_uInt8
* pFMTID
)
554 auto it
= std::find_if(maSections
.begin(), maSections
.end(),
555 [&pFMTID
](const std::unique_ptr
<Section
>& rxSection
) { return memcmp( rxSection
->GetFMTID(), pFMTID
, 16 ) == 0; });
556 if (it
!= maSections
.end())
561 void PropRead::Read()
568 sal_uInt16 mnVersionLo
;
569 sal_uInt16 mnVersionHi
;
571 mpSvStream
->ReadUInt16( mnByteOrder
).ReadUInt16( mnFormat
).ReadUInt16( mnVersionLo
).ReadUInt16( mnVersionHi
);
572 if ( mnByteOrder
!= 0xfffe )
575 std::vector
<sal_uInt8
> aSectCLSID(16);
576 mpSvStream
->ReadBytes(mApplicationCLSID
, 16);
577 sal_uInt32
nSections(0);
578 mpSvStream
->ReadUInt32(nSections
);
579 if ( nSections
> 2 ) // sj: PowerPoint documents are containing max 2 sections
584 for ( sal_uInt32 i
= 0; i
< nSections
; i
++ )
586 mpSvStream
->ReadBytes(aSectCLSID
.data(), aSectCLSID
.size());
587 sal_uInt32
nSectionOfs(0);
588 mpSvStream
->ReadUInt32( nSectionOfs
);
589 sal_uInt64 nCurrent
= mpSvStream
->Tell();
590 if (checkSeek(*mpSvStream
, nSectionOfs
))
592 Section
aSection(aSectCLSID
.data());
593 aSection
.Read(mpSvStream
.get());
594 maSections
.push_back(std::make_unique
<Section
>(aSection
));
596 mpSvStream
->Seek( nCurrent
);
600 PropRead
& PropRead::operator=( const PropRead
& rPropRead
)
602 if ( this != &rPropRead
)
604 mbStatus
= rPropRead
.mbStatus
;
605 mpSvStream
= rPropRead
.mpSvStream
;
607 mnByteOrder
= rPropRead
.mnByteOrder
;
608 memcpy( mApplicationCLSID
, rPropRead
.mApplicationCLSID
, 16 );
610 for(const std::unique_ptr
<Section
>& rSection
: rPropRead
.maSections
)
611 maSections
.push_back(std::make_unique
<Section
>(*rSection
));
616 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */