Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / ole / olestorage.cxx
blobc53f252554d9ff61c4a4a2d99ccd341fb85a3983
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/beans/PropertyValue.hpp>
23 #include <com/sun/star/container/XNameContainer.hpp>
24 #include <com/sun/star/embed/XTransactedObject.hpp>
25 #include <com/sun/star/io/IOException.hpp>
26 #include <com/sun/star/io/NotConnectedException.hpp>
27 #include <com/sun/star/io/TempFile.hpp>
28 #include <com/sun/star/io/XInputStream.hpp>
29 #include <com/sun/star/io/XOutputStream.hpp>
30 #include <com/sun/star/io/XSeekable.hpp>
31 #include <com/sun/star/io/XStream.hpp>
32 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
33 #include <com/sun/star/uno/XComponentContext.hpp>
34 #include <cppuhelper/implbase.hxx>
35 #include <osl/diagnose.h>
36 #include <oox/helper/binaryinputstream.hxx>
37 #include <oox/helper/binaryoutputstream.hxx>
38 #include <oox/helper/containerhelper.hxx>
39 #include <oox/helper/helper.hxx>
41 namespace oox {
42 namespace ole {
44 using namespace ::com::sun::star::beans;
45 using namespace ::com::sun::star::container;
46 using namespace ::com::sun::star::embed;
47 using namespace ::com::sun::star::io;
48 using namespace ::com::sun::star::lang;
49 using namespace ::com::sun::star::uno;
51 namespace {
53 /** Implementation of an OLE storage output stream that inserts itself into the
54 storage when it is closed.
56 class OleOutputStream : public ::cppu::WeakImplHelper< XSeekable, XOutputStream >
58 public:
59 explicit OleOutputStream(
60 const Reference< XComponentContext >& rxContext,
61 const Reference< XNameContainer >& rxStorage,
62 const OUString& rElementName );
64 virtual void SAL_CALL seek( sal_Int64 nPos ) override;
65 virtual sal_Int64 SAL_CALL getPosition() override;
66 virtual sal_Int64 SAL_CALL getLength() override;
68 virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override;
69 virtual void SAL_CALL flush() override;
70 virtual void SAL_CALL closeOutput() override;
72 private:
73 /// @throws IOException
74 void ensureSeekable() const;
75 /// @throws NotConnectedException
76 void ensureConnected() const;
78 private:
79 Reference< XNameContainer > mxStorage;
80 Reference< XStream > mxTempFile;
81 Reference< XOutputStream > mxOutStrm;
82 Reference< XSeekable > mxSeekable;
83 OUString const maElementName;
86 OleOutputStream::OleOutputStream( const Reference< XComponentContext >& rxContext,
87 const Reference< XNameContainer >& rxStorage, const OUString& rElementName ) :
88 mxStorage( rxStorage ),
89 maElementName( rElementName )
91 try
93 mxTempFile.set( TempFile::create(rxContext), UNO_QUERY_THROW );
94 mxOutStrm = mxTempFile->getOutputStream();
95 mxSeekable.set( mxOutStrm, UNO_QUERY );
97 catch(const Exception& )
102 void SAL_CALL OleOutputStream::seek( sal_Int64 nPos )
104 ensureSeekable();
105 mxSeekable->seek( nPos );
108 sal_Int64 SAL_CALL OleOutputStream::getPosition()
110 ensureSeekable();
111 return mxSeekable->getPosition();
114 sal_Int64 SAL_CALL OleOutputStream::getLength()
116 ensureSeekable();
117 return mxSeekable->getLength();
120 void SAL_CALL OleOutputStream::writeBytes( const Sequence< sal_Int8 >& rData )
122 ensureConnected();
123 mxOutStrm->writeBytes( rData );
126 void SAL_CALL OleOutputStream::flush()
128 ensureConnected();
129 mxOutStrm->flush();
132 void SAL_CALL OleOutputStream::closeOutput()
134 ensureConnected();
135 ensureSeekable();
136 // remember the class members
137 Reference< XOutputStream > xOutStrm = mxOutStrm;
138 Reference< XSeekable > xSeekable = mxSeekable;
139 // reset all class members
140 mxOutStrm.clear();
141 mxSeekable.clear();
142 // close stream (and let it throw something if needed)
143 xOutStrm->closeOutput();
144 // on success, insert the stream into the OLE storage (must be seek-ed back before)
145 xSeekable->seek( 0 );
146 if( !ContainerHelper::insertByName( mxStorage, maElementName, Any( mxTempFile ) ) )
147 throw IOException();
150 void OleOutputStream::ensureSeekable() const
152 if( !mxSeekable.is() )
153 throw IOException();
156 void OleOutputStream::ensureConnected() const
158 if( !mxOutStrm.is() )
159 throw NotConnectedException();
162 } // namespace
164 OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
165 const Reference< XInputStream >& rxInStream, bool bBaseStreamAccess ) :
166 StorageBase( rxInStream, bBaseStreamAccess ),
167 mxContext( rxContext ),
168 mpParentStorage( nullptr )
170 OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
171 initStorage( rxInStream );
174 OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
175 const Reference< XStream >& rxOutStream, bool bBaseStreamAccess ) :
176 StorageBase( rxOutStream, bBaseStreamAccess ),
177 mxContext( rxContext ),
178 mpParentStorage( nullptr )
180 OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
181 initStorage( rxOutStream );
184 OleStorage::OleStorage( const OleStorage& rParentStorage,
185 const Reference< XNameContainer >& rxStorage, const OUString& rElementName, bool bReadOnly ) :
186 StorageBase( rParentStorage, rElementName, bReadOnly ),
187 mxContext( rParentStorage.mxContext ),
188 mxStorage( rxStorage ),
189 mpParentStorage( &rParentStorage )
191 OSL_ENSURE( mxStorage.is(), "OleStorage::OleStorage - missing substorage elements" );
194 OleStorage::OleStorage( const OleStorage& rParentStorage,
195 const Reference< XStream >& rxOutStream, const OUString& rElementName ) :
196 StorageBase( rParentStorage, rElementName, false ),
197 mxContext( rParentStorage.mxContext ),
198 mpParentStorage( &rParentStorage )
200 initStorage( rxOutStream );
203 OleStorage::~OleStorage()
207 void OleStorage::initStorage( const Reference< XInputStream >& rxInStream )
209 // if stream is not seekable, create temporary copy
210 Reference< XInputStream > xInStrm = rxInStream;
211 if( !Reference< XSeekable >( xInStrm, UNO_QUERY ).is() ) try
213 Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
215 Reference< XOutputStream > xOutStrm( xTempFile->getOutputStream(), UNO_SET_THROW );
216 /* Pass false to both binary stream objects to keep the UNO
217 streams alive. Life time of these streams is controlled by the
218 tempfile implementation. */
219 BinaryXOutputStream aOutStrm( xOutStrm, false );
220 BinaryXInputStream aInStrm( xInStrm, false );
221 aInStrm.copyToStream( aOutStrm );
222 } // scope closes output stream of tempfile
223 xInStrm = xTempFile->getInputStream();
225 catch(const Exception& )
227 OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" );
230 // create base storage object
231 if( xInStrm.is() ) try
233 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
234 Sequence< Any > aArgs( 2 );
235 aArgs[ 0 ] <<= xInStrm;
236 aArgs[ 1 ] <<= true; // true = do not create a copy of the input stream
237 mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW );
239 catch(const Exception& )
244 void OleStorage::initStorage( const Reference< XStream >& rxOutStream )
246 // create base storage object
247 if( rxOutStream.is() ) try
249 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
250 Sequence< Any > aArgs( 2 );
251 aArgs[ 0 ] <<= rxOutStream;
252 aArgs[ 1 ] <<= true; // true = do not create a copy of the stream
253 mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW );
255 catch(const Exception& )
260 // StorageBase interface ------------------------------------------------------
262 bool OleStorage::implIsStorage() const
264 if( mxStorage.is() ) try
266 /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
267 implementation throws an exception. But we do not return the result
268 of hasElements(), because an empty storage is a valid storage too. */
269 (void)mxStorage->hasElements();
270 return true;
272 catch(const Exception& )
275 return false;
278 Reference< XStorage > OleStorage::implGetXStorage() const
280 OSL_FAIL( "OleStorage::getXStorage - not implemented" );
281 return Reference< XStorage >();
284 void OleStorage::implGetElementNames( ::std::vector< OUString >& orElementNames ) const
286 Sequence< OUString > aNames;
287 if( mxStorage.is() ) try
289 aNames = mxStorage->getElementNames();
290 if( aNames.hasElements() )
291 orElementNames.insert( orElementNames.end(), aNames.begin(), aNames.end() );
293 catch(const Exception& )
298 StorageRef OleStorage::implOpenSubStorage( const OUString& rElementName, bool bCreateMissing )
300 StorageRef xSubStorage;
301 if( mxStorage.is() && !rElementName.isEmpty() )
305 Reference< XNameContainer > xSubElements( mxStorage->getByName( rElementName ), UNO_QUERY_THROW );
306 xSubStorage.reset( new OleStorage( *this, xSubElements, rElementName, true ) );
308 catch(const Exception& )
312 /* The OLESimpleStorage API implementation seems to be buggy in the
313 area of writable inplace substorage (sometimes it overwrites other
314 unrelated streams with zero bytes). We go the save way and create a
315 new OLE storage based on a temporary file. All operations are
316 performed on this clean storage. On committing, the storage will be
317 completely re-inserted into the parent storage. */
318 if( !isReadOnly() && (bCreateMissing || xSubStorage.get()) ) try
320 // create new storage based on a temp file
321 Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
322 StorageRef xTempStorage( new OleStorage( *this, xTempFile, rElementName ) );
323 // copy existing substorage into temp storage
324 if( xSubStorage.get() )
325 xSubStorage->copyStorageToStorage( *xTempStorage );
326 // return the temp storage to caller
327 xSubStorage = xTempStorage;
329 catch(const Exception& )
333 return xSubStorage;
336 Reference< XInputStream > OleStorage::implOpenInputStream( const OUString& rElementName )
338 Reference< XInputStream > xInStream;
339 if( mxStorage.is() ) try
341 xInStream.set( mxStorage->getByName( rElementName ), UNO_QUERY );
343 catch(const Exception& )
346 return xInStream;
349 Reference< XOutputStream > OleStorage::implOpenOutputStream( const OUString& rElementName )
351 Reference< XOutputStream > xOutStream;
352 if( mxStorage.is() && !rElementName.isEmpty() )
353 xOutStream.set( new OleOutputStream( mxContext, mxStorage, rElementName ) );
354 return xOutStream;
357 void OleStorage::implCommit() const
361 // commit this storage (finalizes the file this storage is based on)
362 Reference< XTransactedObject >( mxStorage, UNO_QUERY_THROW )->commit();
363 // re-insert this storage into the parent storage
364 if( mpParentStorage )
366 if( mpParentStorage->mxStorage->hasByName( getName() ) )
368 // replaceByName() does not work (#i109539#)
369 mpParentStorage->mxStorage->removeByName( getName() );
370 Reference< XTransactedObject >( mpParentStorage->mxStorage, UNO_QUERY_THROW )->commit();
372 mpParentStorage->mxStorage->insertByName( getName(), Any( mxStorage ) );
373 // this requires another commit(), which will be performed by the parent storage
376 catch(const Exception& )
381 } // namespace ole
382 } // namespace oox
384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */