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/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>
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
;
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
>
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
;
73 /// @throws IOException
74 void ensureSeekable() const;
75 /// @throws NotConnectedException
76 void ensureConnected() const;
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
)
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
)
105 mxSeekable
->seek( nPos
);
108 sal_Int64 SAL_CALL
OleOutputStream::getPosition()
111 return mxSeekable
->getPosition();
114 sal_Int64 SAL_CALL
OleOutputStream::getLength()
117 return mxSeekable
->getLength();
120 void SAL_CALL
OleOutputStream::writeBytes( const Sequence
< sal_Int8
>& rData
)
123 mxOutStrm
->writeBytes( rData
);
126 void SAL_CALL
OleOutputStream::flush()
132 void SAL_CALL
OleOutputStream::closeOutput()
136 // remember the class members
137 Reference
< XOutputStream
> xOutStrm
= mxOutStrm
;
138 Reference
< XSeekable
> xSeekable
= mxSeekable
;
139 // reset all class members
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
) ) )
150 void OleOutputStream::ensureSeekable() const
152 if( !mxSeekable
.is() )
156 void OleOutputStream::ensureConnected() const
158 if( !mxOutStrm
.is() )
159 throw NotConnectedException();
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();
272 catch(const Exception
& )
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
& )
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
& )
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
) );
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
& )
384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */