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/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>
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
;
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
>
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
;
70 /// @throws IOException
71 void ensureSeekable() const;
72 /// @throws NotConnectedException
73 void ensureConnected() const;
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
))
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
)
102 mxSeekable
->seek( nPos
);
105 sal_Int64 SAL_CALL
OleOutputStream::getPosition()
108 return mxSeekable
->getPosition();
111 sal_Int64 SAL_CALL
OleOutputStream::getLength()
114 return mxSeekable
->getLength();
117 void SAL_CALL
OleOutputStream::writeBytes( const Sequence
< sal_Int8
>& rData
)
120 mxOutStrm
->writeBytes( rData
);
123 void SAL_CALL
OleOutputStream::flush()
129 void SAL_CALL
OleOutputStream::closeOutput()
133 // remember the class members
134 Reference
< XOutputStream
> xOutStrm
= mxOutStrm
;
135 Reference
< XSeekable
> xSeekable
= mxSeekable
;
136 // reset all class members
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
) ) )
147 void OleOutputStream::ensureSeekable() const
149 if( !mxSeekable
.is() )
153 void OleOutputStream::ensureConnected() const
155 if( !mxOutStrm
.is() )
156 throw NotConnectedException();
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();
267 catch(const Exception
& )
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
319 xSubStorage
->copyStorageToStorage( *xTempStorage
);
320 // return the temp storage to caller
321 xSubStorage
= std::move(xTempStorage
);
323 catch(const Exception
& )
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
& )
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
) );
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: */