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 "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/TempFile.hpp>
26 #include <com/sun/star/io/XInputStream.hpp>
27 #include <com/sun/star/io/XOutputStream.hpp>
28 #include <com/sun/star/io/XSeekable.hpp>
29 #include <com/sun/star/io/XStream.hpp>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/uno/XComponentContext.hpp>
32 #include <cppuhelper/implbase.hxx>
33 #include <osl/diagnose.h>
34 #include "oox/helper/binaryinputstream.hxx"
35 #include "oox/helper/binaryoutputstream.hxx"
36 #include "oox/helper/containerhelper.hxx"
37 #include "oox/helper/helper.hxx"
42 using namespace ::com::sun::star::beans
;
43 using namespace ::com::sun::star::container
;
44 using namespace ::com::sun::star::embed
;
45 using namespace ::com::sun::star::io
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star::uno
;
51 /** Implementation of an OLE storage output stream that inserts itself into the
52 storage when it is closed.
54 class OleOutputStream
: public ::cppu::WeakImplHelper
< XSeekable
, XOutputStream
>
57 explicit OleOutputStream(
58 const Reference
< XComponentContext
>& rxContext
,
59 const Reference
< XNameContainer
>& rxStorage
,
60 const OUString
& rElementName
);
61 virtual ~OleOutputStream() override
;
63 virtual void SAL_CALL
seek( sal_Int64 nPos
) throw( IllegalArgumentException
, IOException
, RuntimeException
, std::exception
) override
;
64 virtual sal_Int64 SAL_CALL
getPosition() throw( IOException
, RuntimeException
, std::exception
) override
;
65 virtual sal_Int64 SAL_CALL
getLength() throw( IOException
, RuntimeException
, std::exception
) override
;
67 virtual void SAL_CALL
writeBytes( const Sequence
< sal_Int8
>& rData
) throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
) override
;
68 virtual void SAL_CALL
flush() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
) override
;
69 virtual void SAL_CALL
closeOutput() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
) override
;
72 void ensureSeekable() const throw( IOException
);
73 void ensureConnected() const throw( NotConnectedException
);
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
, const OUString
& rElementName
) :
85 mxStorage( rxStorage
),
86 maElementName( rElementName
)
90 mxTempFile
.set( TempFile::create(rxContext
), UNO_QUERY_THROW
);
91 mxOutStrm
= mxTempFile
->getOutputStream();
92 mxSeekable
.set( mxOutStrm
, UNO_QUERY
);
94 catch(const Exception
& )
99 OleOutputStream::~OleOutputStream()
103 void SAL_CALL
OleOutputStream::seek( sal_Int64 nPos
) throw( IllegalArgumentException
, IOException
, RuntimeException
, std::exception
)
106 mxSeekable
->seek( nPos
);
109 sal_Int64 SAL_CALL
OleOutputStream::getPosition() throw( IOException
, RuntimeException
, std::exception
)
112 return mxSeekable
->getPosition();
115 sal_Int64 SAL_CALL
OleOutputStream::getLength() throw( IOException
, RuntimeException
, std::exception
)
118 return mxSeekable
->getLength();
121 void SAL_CALL
OleOutputStream::writeBytes( const Sequence
< sal_Int8
>& rData
) throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
)
124 mxOutStrm
->writeBytes( rData
);
127 void SAL_CALL
OleOutputStream::flush() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
)
133 void SAL_CALL
OleOutputStream::closeOutput() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
)
137 // remember the class members
138 Reference
< XOutputStream
> xOutStrm
= mxOutStrm
;
139 Reference
< XSeekable
> xSeekable
= mxSeekable
;
140 // reset all class members
143 // close stream (and let it throw something if needed)
144 xOutStrm
->closeOutput();
145 // on success, insert the stream into the OLE storage (must be seek-ed back before)
146 xSeekable
->seek( 0 );
147 if( !ContainerHelper::insertByName( mxStorage
, maElementName
, Any( mxTempFile
) ) )
151 void OleOutputStream::ensureSeekable() const throw( IOException
)
153 if( !mxSeekable
.is() )
157 void OleOutputStream::ensureConnected() const throw( NotConnectedException
)
159 if( !mxOutStrm
.is() )
160 throw NotConnectedException();
165 OleStorage::OleStorage( const Reference
< XComponentContext
>& rxContext
,
166 const Reference
< XInputStream
>& rxInStream
, bool bBaseStreamAccess
) :
167 StorageBase( rxInStream
, bBaseStreamAccess
),
168 mxContext( rxContext
),
169 mpParentStorage( nullptr )
171 OSL_ENSURE( mxContext
.is(), "OleStorage::OleStorage - missing component context" );
172 initStorage( rxInStream
);
175 OleStorage::OleStorage( const Reference
< XComponentContext
>& rxContext
,
176 const Reference
< XStream
>& rxOutStream
, bool bBaseStreamAccess
) :
177 StorageBase( rxOutStream
, bBaseStreamAccess
),
178 mxContext( rxContext
),
179 mpParentStorage( nullptr )
181 OSL_ENSURE( mxContext
.is(), "OleStorage::OleStorage - missing component context" );
182 initStorage( rxOutStream
);
185 OleStorage::OleStorage( const OleStorage
& rParentStorage
,
186 const Reference
< XNameContainer
>& rxStorage
, const OUString
& rElementName
, bool bReadOnly
) :
187 StorageBase( rParentStorage
, rElementName
, bReadOnly
),
188 mxContext( rParentStorage
.mxContext
),
189 mxStorage( rxStorage
),
190 mpParentStorage( &rParentStorage
)
192 OSL_ENSURE( mxStorage
.is(), "OleStorage::OleStorage - missing substorage elements" );
195 OleStorage::OleStorage( const OleStorage
& rParentStorage
,
196 const Reference
< XStream
>& rxOutStream
, const OUString
& rElementName
) :
197 StorageBase( rParentStorage
, rElementName
, false ),
198 mxContext( rParentStorage
.mxContext
),
199 mpParentStorage( &rParentStorage
)
201 initStorage( rxOutStream
);
204 OleStorage::~OleStorage()
208 void OleStorage::initStorage( const Reference
< XInputStream
>& rxInStream
)
210 // if stream is not seekable, create temporary copy
211 Reference
< XInputStream
> xInStrm
= rxInStream
;
212 if( !Reference
< XSeekable
>( xInStrm
, UNO_QUERY
).is() ) try
214 Reference
< XStream
> xTempFile( TempFile::create(mxContext
), UNO_QUERY_THROW
);
216 Reference
< XOutputStream
> xOutStrm( xTempFile
->getOutputStream(), UNO_SET_THROW
);
217 /* Pass false to both binary stream objects to keep the UNO
218 streams alive. Life time of these streams is controlled by the
219 tempfile implementation. */
220 BinaryXOutputStream
aOutStrm( xOutStrm
, false );
221 BinaryXInputStream
aInStrm( xInStrm
, false );
222 aInStrm
.copyToStream( aOutStrm
);
223 } // scope closes output stream of tempfile
224 xInStrm
= xTempFile
->getInputStream();
226 catch(const Exception
& )
228 OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" );
231 // create base storage object
232 if( xInStrm
.is() ) try
234 Reference
< XMultiServiceFactory
> xFactory( mxContext
->getServiceManager(), UNO_QUERY_THROW
);
235 Sequence
< Any
> aArgs( 2 );
236 aArgs
[ 0 ] <<= xInStrm
;
237 aArgs
[ 1 ] <<= true; // true = do not create a copy of the input stream
238 mxStorage
.set( xFactory
->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs
), UNO_QUERY_THROW
);
240 catch(const Exception
& )
245 void OleStorage::initStorage( const Reference
< XStream
>& rxOutStream
)
247 // create base storage object
248 if( rxOutStream
.is() ) try
250 Reference
< XMultiServiceFactory
> xFactory( mxContext
->getServiceManager(), UNO_QUERY_THROW
);
251 Sequence
< Any
> aArgs( 2 );
252 aArgs
[ 0 ] <<= rxOutStream
;
253 aArgs
[ 1 ] <<= true; // true = do not create a copy of the stream
254 mxStorage
.set( xFactory
->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs
), UNO_QUERY_THROW
);
256 catch(const Exception
& )
261 // StorageBase interface ------------------------------------------------------
263 bool OleStorage::implIsStorage() const
265 if( mxStorage
.is() ) try
267 /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
268 implementation throws an exception. But we do not return the result
269 of hasElements(), because an empty storage is a valid storage too. */
270 (void)mxStorage
->hasElements();
273 catch(const Exception
& )
279 Reference
< XStorage
> OleStorage::implGetXStorage() const
281 OSL_FAIL( "OleStorage::getXStorage - not implemented" );
282 return Reference
< XStorage
>();
285 void OleStorage::implGetElementNames( ::std::vector
< OUString
>& orElementNames
) const
287 Sequence
< OUString
> aNames
;
288 if( mxStorage
.is() ) try
290 aNames
= mxStorage
->getElementNames();
291 if( aNames
.getLength() > 0 )
292 orElementNames
.insert( orElementNames
.end(), aNames
.getConstArray(), aNames
.getConstArray() + aNames
.getLength() );
294 catch(const Exception
& )
299 StorageRef
OleStorage::implOpenSubStorage( const OUString
& rElementName
, bool bCreateMissing
)
301 StorageRef xSubStorage
;
302 if( mxStorage
.is() && !rElementName
.isEmpty() )
306 Reference
< XNameContainer
> xSubElements( mxStorage
->getByName( rElementName
), UNO_QUERY_THROW
);
307 xSubStorage
.reset( new OleStorage( *this, xSubElements
, rElementName
, true ) );
309 catch(const Exception
& )
313 /* The OLESimpleStorage API implementation seems to be buggy in the
314 area of writable inplace substorage (sometimes it overwrites other
315 unrelated streams with zero bytes). We go the save way and create a
316 new OLE storage based on a temporary file. All operations are
317 performed on this clean storage. On committing, the storage will be
318 completely re-inserted into the parent storage. */
319 if( !isReadOnly() && (bCreateMissing
|| xSubStorage
.get()) ) try
321 // create new storage based on a temp file
322 Reference
< XStream
> xTempFile( TempFile::create(mxContext
), UNO_QUERY_THROW
);
323 StorageRef
xTempStorage( new OleStorage( *this, xTempFile
, rElementName
) );
324 // copy existing substorage into temp storage
325 if( xSubStorage
.get() )
326 xSubStorage
->copyStorageToStorage( *xTempStorage
);
327 // return the temp storage to caller
328 xSubStorage
= xTempStorage
;
330 catch(const Exception
& )
337 Reference
< XInputStream
> OleStorage::implOpenInputStream( const OUString
& rElementName
)
339 Reference
< XInputStream
> xInStream
;
340 if( mxStorage
.is() ) try
342 xInStream
.set( mxStorage
->getByName( rElementName
), UNO_QUERY
);
344 catch(const Exception
& )
350 Reference
< XOutputStream
> OleStorage::implOpenOutputStream( const OUString
& rElementName
)
352 Reference
< XOutputStream
> xOutStream
;
353 if( mxStorage
.is() && !rElementName
.isEmpty() )
354 xOutStream
.set( new OleOutputStream( mxContext
, mxStorage
, rElementName
) );
358 void OleStorage::implCommit() const
362 // commit this storage (finalizes the file this storage is based on)
363 Reference
< XTransactedObject
>( mxStorage
, UNO_QUERY_THROW
)->commit();
364 // re-insert this storage into the parent storage
365 if( mpParentStorage
)
367 if( mpParentStorage
->mxStorage
->hasByName( getName() ) )
369 // replaceByName() does not work (#i109539#)
370 mpParentStorage
->mxStorage
->removeByName( getName() );
371 Reference
< XTransactedObject
>( mpParentStorage
->mxStorage
, UNO_QUERY_THROW
)->commit();
373 mpParentStorage
->mxStorage
->insertByName( getName(), Any( mxStorage
) );
374 // this requires another commit(), which will be performed by the parent storage
377 catch(const Exception
& )
385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */