sd: keep a non-owning pointer to the OverridingShell
[LibreOffice.git] / oox / source / ole / olestorage.cxx
blobbe7b1c71f3ef2fd1275ae1f7c6924eb46b8b20d9
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 .
20 #include <oox/ole/olestorage.hxx>
22 #include <com/sun/star/container/XNameContainer.hpp>
23 #include <com/sun/star/embed/XTransactedObject.hpp>
24 #include <com/sun/star/io/IOException.hpp>
25 #include <com/sun/star/io/NotConnectedException.hpp>
26 #include <com/sun/star/io/TempFile.hpp>
27 #include <com/sun/star/io/XInputStream.hpp>
28 #include <com/sun/star/io/XOutputStream.hpp>
29 #include <com/sun/star/io/XSeekable.hpp>
30 #include <com/sun/star/io/XStream.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/uno/XComponentContext.hpp>
33 #include <cppuhelper/implbase.hxx>
34 #include <osl/diagnose.h>
35 #include <oox/helper/binaryinputstream.hxx>
36 #include <oox/helper/binaryoutputstream.hxx>
37 #include <oox/helper/containerhelper.hxx>
38 #include <utility>
40 namespace oox::ole {
42 using namespace ::com::sun::star::container;
43 using namespace ::com::sun::star::embed;
44 using namespace ::com::sun::star::io;
45 using namespace ::com::sun::star::lang;
46 using namespace ::com::sun::star::uno;
48 namespace {
50 /** Implementation of an OLE storage output stream that inserts itself into the
51 storage when it is closed.
53 class OleOutputStream : public ::cppu::WeakImplHelper< XSeekable, XOutputStream >
55 public:
56 explicit OleOutputStream(
57 const Reference< XComponentContext >& rxContext,
58 const Reference< XNameContainer >& rxStorage,
59 OUString aElementName );
61 virtual void SAL_CALL seek( sal_Int64 nPos ) override;
62 virtual sal_Int64 SAL_CALL getPosition() override;
63 virtual sal_Int64 SAL_CALL getLength() override;
65 virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override;
66 virtual void SAL_CALL flush() override;
67 virtual void SAL_CALL closeOutput() override;
69 private:
70 /// @throws IOException
71 void ensureSeekable() const;
72 /// @throws NotConnectedException
73 void ensureConnected() const;
75 private:
76 Reference< XNameContainer > mxStorage;
77 Reference< XStream > mxTempFile;
78 Reference< XOutputStream > mxOutStrm;
79 Reference< XSeekable > mxSeekable;
80 OUString maElementName;
83 OleOutputStream::OleOutputStream( const Reference< XComponentContext >& rxContext,
84 const Reference< XNameContainer >& rxStorage, OUString aElementName ) :
85 mxStorage( rxStorage ),
86 maElementName(std::move( aElementName ))
88 try
90 mxTempFile.set( TempFile::create(rxContext), UNO_QUERY_THROW );
91 mxOutStrm = mxTempFile->getOutputStream();
92 mxSeekable.set( mxOutStrm, UNO_QUERY );
94 catch(const Exception& )
99 void SAL_CALL OleOutputStream::seek( sal_Int64 nPos )
101 ensureSeekable();
102 mxSeekable->seek( nPos );
105 sal_Int64 SAL_CALL OleOutputStream::getPosition()
107 ensureSeekable();
108 return mxSeekable->getPosition();
111 sal_Int64 SAL_CALL OleOutputStream::getLength()
113 ensureSeekable();
114 return mxSeekable->getLength();
117 void SAL_CALL OleOutputStream::writeBytes( const Sequence< sal_Int8 >& rData )
119 ensureConnected();
120 mxOutStrm->writeBytes( rData );
123 void SAL_CALL OleOutputStream::flush()
125 ensureConnected();
126 mxOutStrm->flush();
129 void SAL_CALL OleOutputStream::closeOutput()
131 ensureConnected();
132 ensureSeekable();
133 // remember the class members
134 Reference< XOutputStream > xOutStrm = mxOutStrm;
135 Reference< XSeekable > xSeekable = mxSeekable;
136 // reset all class members
137 mxOutStrm.clear();
138 mxSeekable.clear();
139 // close stream (and let it throw something if needed)
140 xOutStrm->closeOutput();
141 // on success, insert the stream into the OLE storage (must be seek-ed back before)
142 xSeekable->seek( 0 );
143 if( !ContainerHelper::insertByName( mxStorage, maElementName, Any( mxTempFile ) ) )
144 throw IOException();
147 void OleOutputStream::ensureSeekable() const
149 if( !mxSeekable.is() )
150 throw IOException();
153 void OleOutputStream::ensureConnected() const
155 if( !mxOutStrm.is() )
156 throw NotConnectedException();
159 } // namespace
161 OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
162 const Reference< XInputStream >& rxInStream, bool bBaseStreamAccess ) :
163 StorageBase( rxInStream, bBaseStreamAccess ),
164 mxContext( rxContext ),
165 mpParentStorage( nullptr )
167 OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
168 initStorage( rxInStream );
171 OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
172 const Reference< XStream >& rxOutStream, bool bBaseStreamAccess ) :
173 StorageBase( rxOutStream, bBaseStreamAccess ),
174 mxContext( rxContext ),
175 mpParentStorage( nullptr )
177 OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
178 initStorage( rxOutStream );
181 OleStorage::OleStorage( const OleStorage& rParentStorage,
182 const Reference< XNameContainer >& rxStorage, const OUString& rElementName, bool bReadOnly ) :
183 StorageBase( rParentStorage, rElementName, bReadOnly ),
184 mxContext( rParentStorage.mxContext ),
185 mxStorage( rxStorage ),
186 mpParentStorage( &rParentStorage )
188 OSL_ENSURE( mxStorage.is(), "OleStorage::OleStorage - missing substorage elements" );
191 OleStorage::OleStorage( const OleStorage& rParentStorage,
192 const Reference< XStream >& rxOutStream, const OUString& rElementName ) :
193 StorageBase( rParentStorage, rElementName, false ),
194 mxContext( rParentStorage.mxContext ),
195 mpParentStorage( &rParentStorage )
197 initStorage( rxOutStream );
200 OleStorage::~OleStorage()
204 void OleStorage::initStorage( const Reference< XInputStream >& rxInStream )
206 // if stream is not seekable, create temporary copy
207 Reference< XInputStream > xInStrm = rxInStream;
208 if( !Reference< XSeekable >( xInStrm, UNO_QUERY ).is() ) try
210 Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
212 Reference< XOutputStream > xOutStrm( xTempFile->getOutputStream(), UNO_SET_THROW );
213 /* Pass false to both binary stream objects to keep the UNO
214 streams alive. Life time of these streams is controlled by the
215 tempfile implementation. */
216 BinaryXOutputStream aOutStrm( xOutStrm, false );
217 BinaryXInputStream aInStrm( xInStrm, false );
218 aInStrm.copyToStream( aOutStrm );
219 } // scope closes output stream of tempfile
220 xInStrm = xTempFile->getInputStream();
222 catch(const Exception& )
224 OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" );
227 // create base storage object
228 if( xInStrm.is() ) try
230 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
231 Sequence< Any > aArgs{ Any(xInStrm),
232 Any(true) };// true = do not create a copy of the input stream
233 mxStorage.set( xFactory->createInstanceWithArguments(u"com.sun.star.embed.OLESimpleStorage"_ustr, aArgs ), UNO_QUERY_THROW );
235 catch(const Exception& )
240 void OleStorage::initStorage( const Reference< XStream >& rxOutStream )
242 // create base storage object
243 if( rxOutStream.is() ) try
245 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
246 Sequence< Any > aArgs{ Any(rxOutStream),
247 Any(true) }; // true = do not create a copy of the stream
248 mxStorage.set( xFactory->createInstanceWithArguments(u"com.sun.star.embed.OLESimpleStorage"_ustr, aArgs ), UNO_QUERY_THROW );
250 catch(const Exception& )
255 // StorageBase interface ------------------------------------------------------
257 bool OleStorage::implIsStorage() const
259 if( mxStorage.is() ) try
261 /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
262 implementation throws an exception. But we do not return the result
263 of hasElements(), because an empty storage is a valid storage too. */
264 (void)mxStorage->hasElements();
265 return true;
267 catch(const Exception& )
270 return false;
273 Reference< XStorage > OleStorage::implGetXStorage() const
275 OSL_FAIL( "OleStorage::getXStorage - not implemented" );
276 return Reference< XStorage >();
279 void OleStorage::implGetElementNames( ::std::vector< OUString >& orElementNames ) const
281 if( mxStorage.is() ) try
283 const Sequence<OUString> aNames = mxStorage->getElementNames();
284 if( aNames.hasElements() )
285 orElementNames.insert( orElementNames.end(), aNames.begin(), aNames.end() );
287 catch(const Exception& )
292 StorageRef OleStorage::implOpenSubStorage( const OUString& rElementName, bool bCreateMissing )
294 StorageRef xSubStorage;
295 if( mxStorage.is() && !rElementName.isEmpty() )
299 Reference< XNameContainer > xSubElements( mxStorage->getByName( rElementName ), UNO_QUERY_THROW );
300 xSubStorage.reset( new OleStorage( *this, xSubElements, rElementName, true ) );
302 catch(const Exception& )
306 /* The OLESimpleStorage API implementation seems to be buggy in the
307 area of writable inplace substorage (sometimes it overwrites other
308 unrelated streams with zero bytes). We go the save way and create a
309 new OLE storage based on a temporary file. All operations are
310 performed on this clean storage. On committing, the storage will be
311 completely re-inserted into the parent storage. */
312 if( !isReadOnly() && (bCreateMissing || xSubStorage) ) try
314 // create new storage based on a temp file
315 Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
316 StorageRef xTempStorage( new OleStorage( *this, xTempFile, rElementName ) );
317 // copy existing substorage into temp storage
318 if( xSubStorage )
319 xSubStorage->copyStorageToStorage( *xTempStorage );
320 // return the temp storage to caller
321 xSubStorage = std::move(xTempStorage);
323 catch(const Exception& )
327 return xSubStorage;
330 Reference< XInputStream > OleStorage::implOpenInputStream( const OUString& rElementName )
332 Reference< XInputStream > xInStream;
333 if( mxStorage.is() ) try
335 xInStream.set( mxStorage->getByName( rElementName ), UNO_QUERY );
337 catch(const Exception& )
340 return xInStream;
343 Reference< XOutputStream > OleStorage::implOpenOutputStream( const OUString& rElementName )
345 Reference< XOutputStream > xOutStream;
346 if( mxStorage.is() && !rElementName.isEmpty() )
347 xOutStream.set( new OleOutputStream( mxContext, mxStorage, rElementName ) );
348 return xOutStream;
351 void OleStorage::implCommit() const
355 // commit this storage (finalizes the file this storage is based on)
356 Reference< XTransactedObject >( mxStorage, UNO_QUERY_THROW )->commit();
357 // re-insert this storage into the parent storage
358 if( mpParentStorage )
360 if( mpParentStorage->mxStorage->hasByName( getName() ) )
362 // replaceByName() does not work (#i109539#)
363 mpParentStorage->mxStorage->removeByName( getName() );
364 Reference< XTransactedObject >( mpParentStorage->mxStorage, UNO_QUERY_THROW )->commit();
366 mpParentStorage->mxStorage->insertByName( getName(), Any( mxStorage ) );
367 // this requires another commit(), which will be performed by the parent storage
370 catch(const Exception& )
375 } // namespace oox::ole
377 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */