summaryrefslogtreecommitdiffstats
path: root/sd/source/filter/ppt/propread.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sd/source/filter/ppt/propread.cxx')
-rw-r--r--sd/source/filter/ppt/propread.cxx615
1 files changed, 615 insertions, 0 deletions
diff --git a/sd/source/filter/ppt/propread.cxx b/sd/source/filter/ppt/propread.cxx
new file mode 100644
index 0000000000..b9fb96f988
--- /dev/null
+++ b/sd/source/filter/ppt/propread.cxx
@@ -0,0 +1,615 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "propread.hxx"
+#include <rtl/tencinfo.h>
+#include <rtl/textenc.h>
+#include <sal/log.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <osl/diagnose.h>
+#include <memory>
+
+PropEntry::PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ) :
+ mnId ( nId ),
+ mnSize ( nBufSize ),
+ mpBuf ( new sal_uInt8[ nBufSize ] )
+{
+ memcpy( mpBuf.get(), pBuf, nBufSize );
+};
+
+PropEntry::PropEntry( const PropEntry& rProp ) :
+ mnId ( rProp.mnId ),
+ mnSize ( rProp.mnSize ),
+ mpBuf ( new sal_uInt8[ mnSize ] )
+{
+ memcpy( mpBuf.get(), rProp.mpBuf.get(), mnSize );
+};
+
+PropEntry& PropEntry::operator=(const PropEntry& rPropEntry)
+{
+ if ( this != &rPropEntry )
+ {
+ mnId = rPropEntry.mnId;
+ mnSize = rPropEntry.mnSize;
+ mpBuf.reset( new sal_uInt8[ mnSize ] );
+ memcpy( mpBuf.get(), rPropEntry.mpBuf.get(), mnSize );
+ }
+ return *this;
+}
+
+void PropItem::Clear()
+{
+ Seek( STREAM_SEEK_TO_BEGIN );
+ delete[] static_cast<sal_uInt8*>(SwitchBuffer());
+}
+
+static sal_Int32 lcl_getMaxSafeStrLen(sal_uInt32 nSize)
+{
+ nSize -= 1; //Drop NULL terminator
+
+ //If it won't fit in a string, clip it to the max size that does
+ if (nSize > SAL_MAX_INT32)
+ nSize = SAL_MAX_INT32;
+
+ return static_cast< sal_Int32 >( nSize );
+}
+
+bool PropItem::Read( OUString& rString, sal_uInt32 nStringType, bool bAlign )
+{
+ sal_uInt32 nType, nItemPos;
+ bool bRetValue = false;
+
+ nItemPos = Tell();
+
+ if ( nStringType == VT_EMPTY )
+ {
+ nType = VT_NULL; // Initialize in case stream fails.
+ ReadUInt32( nType );
+ }
+ else
+ nType = nStringType & VT_TYPEMASK;
+
+ sal_uInt32 nItemSize(0); // Initialize in case stream fails.
+ ReadUInt32(nItemSize);
+
+ switch( nType )
+ {
+ case VT_LPSTR :
+ {
+ if (nItemSize)
+ {
+ auto nMaxSizePossible = remainingSize();
+ if (nItemSize > nMaxSizePossible)
+ {
+ SAL_WARN("sd.filter", "String of Len " << nItemSize << " claimed, only " << nMaxSizePossible << " possible");
+ nItemSize = nMaxSizePossible;
+ }
+ }
+
+ if (nItemSize)
+ {
+ try
+ {
+ std::unique_ptr<char[]> pString( new char[ nItemSize ] );
+ if ( mnTextEnc == RTL_TEXTENCODING_UCS2 )
+ {
+ nItemSize >>= 1;
+ if ( nItemSize > 1 )
+ {
+ sal_Unicode* pWString = reinterpret_cast<sal_Unicode*>(pString.get());
+ for (sal_uInt32 i = 0; i < nItemSize; ++i)
+ ReadUtf16( pWString[ i ] );
+ rString = OUString(pWString, lcl_getMaxSafeStrLen(nItemSize));
+ }
+ else
+ rString.clear();
+ bRetValue = true;
+ }
+ else
+ {
+ SvMemoryStream::ReadBytes(pString.get(), nItemSize);
+ if ( pString[ nItemSize - 1 ] == 0 )
+ {
+ if ( nItemSize > 1 )
+ rString = OUString(pString.get(), rtl_str_getLength(pString.get()), mnTextEnc);
+ else
+ rString.clear();
+ bRetValue = true;
+ }
+ }
+ }
+ catch( const std::bad_alloc& )
+ {
+ OSL_FAIL( "sd PropItem::Read bad alloc" );
+ }
+ }
+ if ( bAlign )
+ SeekRel( ( 4 - ( nItemSize & 3 ) ) & 3 ); // dword align
+ }
+ break;
+
+ case VT_LPWSTR :
+ {
+ if (nItemSize)
+ {
+ auto nMaxSizePossible = remainingSize() / sizeof(sal_Unicode);
+ if (nItemSize > nMaxSizePossible)
+ {
+ SAL_WARN("sd.filter", "String of Len " << nItemSize << " claimed, only " << nMaxSizePossible << " possible");
+ nItemSize = nMaxSizePossible;
+ }
+ }
+
+ if (nItemSize)
+ {
+ try
+ {
+ std::unique_ptr<sal_Unicode[]> pString( new sal_Unicode[ nItemSize ] );
+ for (sal_uInt32 i = 0; i < nItemSize; ++i)
+ ReadUtf16( pString[ i ] );
+ if ( pString[ nItemSize - 1 ] == 0 )
+ {
+ if ( static_cast<sal_uInt16>(nItemSize) > 1 )
+ rString = OUString(pString.get(), lcl_getMaxSafeStrLen(nItemSize));
+ else
+ rString.clear();
+ bRetValue = true;
+ }
+ }
+ catch( const std::bad_alloc& )
+ {
+ OSL_FAIL( "sd PropItem::Read bad alloc" );
+ }
+ }
+ if ( bAlign && ( nItemSize & 1 ) )
+ SeekRel( 2 ); // dword align
+ }
+ break;
+ }
+ if ( !bRetValue )
+ Seek( nItemPos );
+ return bRetValue;
+}
+
+PropItem& PropItem::operator=( PropItem& rPropItem )
+{
+ if ( this != &rPropItem )
+ {
+ Seek( STREAM_SEEK_TO_BEGIN );
+ delete[] static_cast<sal_uInt8*>(SwitchBuffer());
+
+ mnTextEnc = rPropItem.mnTextEnc;
+ SvMemoryStream::WriteBytes(rPropItem.GetData(), rPropItem.TellEnd());
+ }
+ return *this;
+}
+
+Section::Section( const Section& rSection )
+ : mnTextEnc(rSection.mnTextEnc)
+{
+ for ( int i = 0; i < 16; i++ )
+ aFMTID[ i ] = rSection.aFMTID[ i ];
+ for(const std::unique_ptr<PropEntry>& rEntry : rSection.maEntries)
+ maEntries.push_back(std::make_unique<PropEntry>(*rEntry));
+}
+
+Section::Section( const sal_uInt8* pFMTID ) : mnTextEnc(RTL_TEXTENCODING_MS_1252)
+{
+ for ( int i = 0; i < 16; i++ )
+ aFMTID[ i ] = pFMTID[ i ];
+}
+
+bool Section::GetProperty( sal_uInt32 nId, PropItem& rPropItem )
+{
+ if ( nId )
+ {
+ auto iter = std::find_if(maEntries.begin(), maEntries.end(),
+ [nId](const std::unique_ptr<PropEntry>& rxEntry) { return rxEntry->mnId == nId; });
+
+ if (iter != maEntries.end())
+ {
+ rPropItem.Clear();
+ rPropItem.SetTextEncoding( mnTextEnc );
+ rPropItem.WriteBytes( (*iter)->mpBuf.get(), (*iter)->mnSize );
+ rPropItem.Seek( STREAM_SEEK_TO_BEGIN );
+ return true;
+ }
+ }
+ return false;
+}
+
+void Section::AddProperty( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize )
+{
+ // just a simple id check
+
+ if ( !nId )
+ return;
+ if ( nId == 0xffffffff )
+ nId = 0;
+
+ // do not allow same PropId's, sort
+ auto iter = std::find_if(maEntries.begin(), maEntries.end(),
+ [nId](const std::unique_ptr<PropEntry>& rxEntry) { return rxEntry->mnId >= nId; });
+ if (iter != maEntries.end())
+ {
+ if ( (*iter)->mnId == nId )
+ (*iter).reset(new PropEntry( nId, pBuf, nBufSize ));
+ else
+ maEntries.insert( iter, std::make_unique<PropEntry>( nId, pBuf, nBufSize ));
+ }
+ else
+ {
+ maEntries.push_back( std::make_unique<PropEntry>( nId, pBuf, nBufSize ) );
+ }
+}
+
+void Section::GetDictionary(PropDictionary& rDict)
+{
+ auto iter = std::find_if(maEntries.begin(), maEntries.end(),
+ [](const std::unique_ptr<PropEntry>& rxEntry) { return rxEntry->mnId == 0; });
+
+ if (iter == maEntries.end())
+ return;
+
+ SvMemoryStream aStream( (*iter)->mpBuf.get(), (*iter)->mnSize, StreamMode::READ );
+ aStream.Seek( STREAM_SEEK_TO_BEGIN );
+ sal_uInt32 nDictCount(0);
+ aStream.ReadUInt32( nDictCount );
+ for (sal_uInt32 i = 0; i < nDictCount; ++i)
+ {
+ sal_uInt32 nId(0), nSize(0);
+ aStream.ReadUInt32(nId).ReadUInt32(nSize);
+ if (!aStream.good() || nSize > aStream.remainingSize())
+ break;
+ if (mnTextEnc == RTL_TEXTENCODING_UCS2)
+ nSize >>= 1;
+ if (!nSize)
+ continue;
+ OUString aString;
+ try
+ {
+ if ( mnTextEnc == RTL_TEXTENCODING_UCS2 )
+ {
+ std::unique_ptr<sal_Unicode[]> pWString( new sal_Unicode[nSize] );
+ for (sal_uInt32 j = 0; j < nSize; ++j)
+ aStream.ReadUtf16(pWString[j]);
+ aString = OUString(pWString.get(), lcl_getMaxSafeStrLen(nSize));
+ }
+ else
+ {
+ std::unique_ptr<char[]> pString( new char[nSize] );
+ aStream.ReadBytes(pString.get(), nSize);
+ aString = OUString(pString.get(), lcl_getMaxSafeStrLen(nSize), mnTextEnc);
+ }
+ }
+ catch( const std::bad_alloc& )
+ {
+ OSL_FAIL( "sd Section::GetDictionary bad alloc" );
+ }
+ if (aString.isEmpty())
+ break;
+ rDict.insert( std::make_pair(aString,nId) );
+ }
+}
+
+void Section::Read( SotStorageStream *pStrm )
+{
+ sal_uInt64 nSecOfs = pStrm->Tell();
+ sal_uInt64 nStrmSize = pStrm->remainingSize();
+
+ mnTextEnc = RTL_TEXTENCODING_MS_1252;
+ sal_uInt32 nSecSize(0), nPropCount(0);
+ pStrm->ReadUInt32(nSecSize).ReadUInt32(nPropCount);
+ if (nSecSize > nStrmSize)
+ {
+ SAL_WARN("sd.filter", "Section Len " << nSecSize << " claimed, only " << nStrmSize << " possible");
+ nSecSize = nStrmSize;
+ }
+
+ while (nPropCount--)
+ {
+ sal_uInt32 nPropId(0), nPropOfs(0);
+ pStrm->ReadUInt32(nPropId).ReadUInt32(nPropOfs);
+ if (!pStrm->good())
+ break;
+ auto nCurrent = pStrm->Tell();
+ sal_uInt64 nOffset = nPropOfs + nSecOfs;
+ if (!checkSeek(*pStrm, nOffset))
+ break;
+ if ( nPropId ) // do not read dictionary
+ {
+ sal_uInt32 nPropType(0), nVectorCount(0);
+ pStrm->ReadUInt32(nPropType);
+
+ sal_uInt32 nPropSize = 4;
+ if ( nPropType & VT_VECTOR )
+ {
+ pStrm->ReadUInt32( nVectorCount );
+ nPropType &=~VT_VECTOR;
+ nPropSize += 4;
+ }
+ else
+ nVectorCount = 1;
+
+ bool bVariant = ( nPropType == VT_VARIANT );
+
+ o3tl::sorted_vector<sal_uInt64> aVisitedOffsets;
+
+ for (sal_uInt32 i = 0; nPropSize && i < nVectorCount && pStrm->good(); ++i)
+ {
+ if ( bVariant )
+ {
+ pStrm->ReadUInt32( nPropType );
+ nPropSize += 4;
+ }
+ sal_uInt32 nTemp(0);
+ switch( nPropType )
+ {
+ case VT_UI1 :
+ nPropSize++;
+ break;
+
+ case VT_I2 :
+ case VT_UI2 :
+ case VT_BOOL :
+ nPropSize += 2;
+ break;
+
+ case VT_I4 :
+ case VT_R4 :
+ case VT_UI4 :
+ case VT_ERROR :
+ nPropSize += 4;
+ break;
+
+ case VT_I8 :
+ case VT_R8 :
+ case VT_CY :
+ case VT_UI8 :
+ case VT_DATE :
+ case VT_FILETIME :
+ nPropSize += 8;
+ break;
+
+ case VT_BSTR :
+ pStrm->ReadUInt32( nTemp );
+ nPropSize += ( nTemp + 4 );
+ break;
+
+ case VT_LPSTR :
+ pStrm->ReadUInt32( nTemp );
+ nPropSize += ( nTemp + 4 );
+ break;
+
+ case VT_LPWSTR :
+ {
+ pStrm->ReadUInt32( nTemp );
+ // looks like these are aligned to 4 bytes
+ sal_uInt64 nLength = nPropOfs + nSecOfs + nPropSize + ( nTemp << 1 ) + 4;
+ nPropSize += ( nTemp << 1 ) + 4 + (nLength % 4);
+ }
+ break;
+
+ case VT_BLOB_OBJECT :
+ case VT_BLOB :
+ case VT_CF :
+ pStrm->ReadUInt32( nTemp );
+ nPropSize += ( nTemp + 4 );
+ break;
+
+ case VT_CLSID :
+ case VT_STREAM :
+ case VT_STORAGE :
+ case VT_STREAMED_OBJECT :
+ case VT_STORED_OBJECT :
+ case VT_VARIANT :
+ case VT_VECTOR :
+ default :
+ nPropSize = 0;
+ }
+ if ( nPropSize )
+ {
+ if ( ( nVectorCount - i ) > 1 )
+ {
+ nOffset = nPropOfs + nSecOfs + nPropSize;
+ if (!checkSeek(*pStrm, nOffset))
+ break;
+ // inserts returns false if an equivalent element already existed
+ if (!aVisitedOffsets.insert(nOffset).second)
+ {
+ SAL_WARN("sd.filter", "loop in Section::Read property list");
+ break;
+ }
+ }
+ }
+ else
+ break;
+ }
+ if ( nPropSize )
+ {
+ if ( nPropSize > nStrmSize )
+ {
+ break;
+ }
+ pStrm->Seek( nPropOfs + nSecOfs );
+ // make sure we don't overflow the section size
+ if( nPropSize > nSecSize - nSecOfs )
+ nPropSize = nSecSize - nSecOfs;
+ std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ nPropSize ] );
+ nPropSize = pStrm->ReadBytes(pBuf.get(), nPropSize);
+ AddProperty( nPropId, pBuf.get(), nPropSize );
+ }
+ if ( nPropId == 1 )
+ {
+ PropItem aPropItem;
+ if ( GetProperty( 1, aPropItem ) )
+ {
+ aPropItem.ReadUInt32( nPropType );
+ if ( nPropType == VT_I2 )
+ {
+ sal_uInt16 nCodePage(0);
+ aPropItem.ReadUInt16(nCodePage);
+
+ if ( nCodePage == 1200 )
+ {
+ mnTextEnc = RTL_TEXTENCODING_UCS2;
+ }
+ else
+ {
+ mnTextEnc = rtl_getTextEncodingFromWindowsCodePage( nCodePage );
+ if ( mnTextEnc == RTL_TEXTENCODING_DONTKNOW )
+ mnTextEnc = RTL_TEXTENCODING_MS_1252;
+ }
+ }
+ else
+ {
+ mnTextEnc = RTL_TEXTENCODING_MS_1252;
+ }
+ }
+ }
+ }
+ else
+ {
+ sal_uInt32 nDictCount(0);
+ pStrm->ReadUInt32(nDictCount);
+ auto nMaxRecordsPossible = pStrm->remainingSize() / (sizeof(sal_uInt32)*2);
+ if (nDictCount > nMaxRecordsPossible)
+ {
+ SAL_WARN("sd.filter", "Dictionary count of " << nDictCount << " claimed, only " << nMaxRecordsPossible << " possible");
+ nDictCount = nMaxRecordsPossible;
+ }
+ for (sal_uInt32 i = 0; i < nDictCount; ++i)
+ {
+ sal_uInt32 nSize(0);
+ pStrm->ReadUInt32( nSize ).ReadUInt32( nSize );
+ if (!pStrm->good())
+ break;
+ sal_uInt64 nPos = pStrm->Tell() + nSize;
+ if (!checkSeek(*pStrm, nPos))
+ break;
+ }
+ sal_uInt64 nSize = pStrm->Tell();
+ pStrm->Seek( nPropOfs + nSecOfs );
+ nSize -= pStrm->Tell();
+ if ( nSize > nStrmSize )
+ {
+ break;
+ }
+ std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ nSize ] );
+ nSize = pStrm->ReadBytes(pBuf.get(), nSize);
+ AddProperty( 0xffffffff, pBuf.get(), nSize );
+ }
+ pStrm->Seek(nCurrent);
+ }
+ pStrm->Seek(nSecOfs + nSecSize);
+}
+
+Section& Section::operator=( const Section& rSection )
+{
+ if ( this != &rSection )
+ {
+ memcpy( static_cast<void*>(aFMTID), static_cast<void const *>(rSection.aFMTID), 16 );
+
+ for(const std::unique_ptr<PropEntry>& rEntry : rSection.maEntries)
+ maEntries.push_back(std::make_unique<PropEntry>(*rEntry));
+ }
+ return *this;
+}
+
+PropRead::PropRead( SotStorage& rStorage, const OUString& rName ) :
+ mbStatus ( false ),
+ mnByteOrder ( 0xfffe )
+{
+ if ( rStorage.IsStream( rName ) )
+ {
+ mpSvStream = rStorage.OpenSotStream( rName, StreamMode::STD_READ );
+ if ( mpSvStream.is() )
+ {
+ mpSvStream->SetEndian( SvStreamEndian::LITTLE );
+ memset( mApplicationCLSID, 0, 16 );
+ mbStatus = true;
+ }
+ }
+}
+
+const Section* PropRead::GetSection( const sal_uInt8* pFMTID )
+{
+ auto it = std::find_if(maSections.begin(), maSections.end(),
+ [&pFMTID](const std::unique_ptr<Section>& rxSection) { return memcmp( rxSection->GetFMTID(), pFMTID, 16 ) == 0; });
+ if (it != maSections.end())
+ return it->get();
+ return nullptr;
+}
+
+void PropRead::Read()
+{
+ maSections.clear();
+
+ if ( !mbStatus )
+ return;
+
+ sal_uInt16 mnVersionLo;
+ sal_uInt16 mnVersionHi;
+ sal_uInt16 mnFormat;
+ mpSvStream->ReadUInt16( mnByteOrder ).ReadUInt16( mnFormat ).ReadUInt16( mnVersionLo ).ReadUInt16( mnVersionHi );
+ if ( mnByteOrder != 0xfffe )
+ return;
+
+ std::vector<sal_uInt8> aSectCLSID(16);
+ mpSvStream->ReadBytes(mApplicationCLSID, 16);
+ sal_uInt32 nSections(0);
+ mpSvStream->ReadUInt32(nSections);
+ if ( nSections > 2 ) // sj: PowerPoint documents are containing max 2 sections
+ {
+ mbStatus = false;
+ }
+ else
+ for ( sal_uInt32 i = 0; i < nSections; i++ )
+ {
+ mpSvStream->ReadBytes(aSectCLSID.data(), aSectCLSID.size());
+ sal_uInt32 nSectionOfs(0);
+ mpSvStream->ReadUInt32( nSectionOfs );
+ sal_uInt64 nCurrent = mpSvStream->Tell();
+ if (checkSeek(*mpSvStream, nSectionOfs))
+ {
+ Section aSection(aSectCLSID.data());
+ aSection.Read(mpSvStream.get());
+ maSections.push_back(std::make_unique<Section>(aSection));
+ }
+ mpSvStream->Seek( nCurrent );
+ }
+}
+
+PropRead& PropRead::operator=( const PropRead& rPropRead )
+{
+ if ( this != &rPropRead )
+ {
+ mbStatus = rPropRead.mbStatus;
+ mpSvStream = rPropRead.mpSvStream;
+
+ mnByteOrder = rPropRead.mnByteOrder;
+ memcpy( mApplicationCLSID, rPropRead.mApplicationCLSID, 16 );
+
+ for(const std::unique_ptr<Section>& rSection : rPropRead.maSections)
+ maSections.push_back(std::make_unique<Section>(*rSection));
+ }
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */