Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
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/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
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 .
18 : : */
19 : :
20 : : #include "oox/ole/olestorage.hxx"
21 : :
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/XInputStream.hpp>
26 : : #include <com/sun/star/io/XOutputStream.hpp>
27 : : #include <com/sun/star/io/XSeekable.hpp>
28 : : #include <com/sun/star/io/XStream.hpp>
29 : : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
30 : : #include <com/sun/star/uno/XComponentContext.hpp>
31 : : #include <cppuhelper/implbase2.hxx>
32 : : #include "oox/helper/binaryinputstream.hxx"
33 : : #include "oox/helper/binaryoutputstream.hxx"
34 : : #include "oox/helper/containerhelper.hxx"
35 : : #include "oox/helper/helper.hxx"
36 : :
37 : : namespace oox {
38 : : namespace ole {
39 : :
40 : : // ============================================================================
41 : :
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;
48 : :
49 : : using ::rtl::OUString;
50 : :
51 : : // ============================================================================
52 : :
53 : : namespace {
54 : :
55 : : typedef ::cppu::WeakImplHelper2< XSeekable, XOutputStream > OleOutputStreamBase;
56 : :
57 : : /** Implementation of an OLE storage output stream that inserts itself into the
58 : : storage when it is closed.
59 : : */
60 : : class OleOutputStream : public OleOutputStreamBase
61 : : {
62 : : public:
63 : : explicit OleOutputStream(
64 : : const Reference< XComponentContext >& rxContext,
65 : : const Reference< XNameContainer >& rxStorage,
66 : : const OUString& rElementName );
67 : : virtual ~OleOutputStream();
68 : :
69 : : virtual void SAL_CALL seek( sal_Int64 nPos ) throw( IllegalArgumentException, IOException, RuntimeException );
70 : : virtual sal_Int64 SAL_CALL getPosition() throw( IOException, RuntimeException );
71 : : virtual sal_Int64 SAL_CALL getLength() throw( IOException, RuntimeException );
72 : :
73 : : virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException );
74 : : virtual void SAL_CALL flush() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException );
75 : : virtual void SAL_CALL closeOutput() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException );
76 : :
77 : : private:
78 : : void ensureSeekable() const throw( IOException );
79 : : void ensureConnected() const throw( NotConnectedException );
80 : :
81 : : private:
82 : : Reference< XNameContainer > mxStorage;
83 : : Reference< XStream > mxTempFile;
84 : : Reference< XOutputStream > mxOutStrm;
85 : : Reference< XSeekable > mxSeekable;
86 : : OUString maElementName;
87 : : };
88 : :
89 : : // ----------------------------------------------------------------------------
90 : :
91 : 209 : OleOutputStream::OleOutputStream( const Reference< XComponentContext >& rxContext,
92 : : const Reference< XNameContainer >& rxStorage, const OUString& rElementName ) :
93 : : mxStorage( rxStorage ),
94 : 209 : maElementName( rElementName )
95 : : {
96 : : try
97 : : {
98 [ + - ][ + - ]: 209 : Reference< XMultiServiceFactory > xFactory( rxContext->getServiceManager(), UNO_QUERY_THROW );
[ + - ]
99 [ + - ][ + - ]: 209 : mxTempFile.set( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.io.TempFile" ) ), UNO_QUERY_THROW );
[ + - ][ + - ]
100 [ + - ][ + - ]: 209 : mxOutStrm = mxTempFile->getOutputStream();
[ + - ]
101 [ # # ][ + - ]: 209 : mxSeekable.set( mxOutStrm, UNO_QUERY );
102 : : }
103 [ # # ]: 0 : catch(const Exception& )
104 : : {
105 : : }
106 : 209 : }
107 : :
108 : 209 : OleOutputStream::~OleOutputStream()
109 : : {
110 [ - + ]: 418 : }
111 : :
112 : 0 : void SAL_CALL OleOutputStream::seek( sal_Int64 nPos ) throw( IllegalArgumentException, IOException, RuntimeException )
113 : : {
114 : 0 : ensureSeekable();
115 : 0 : mxSeekable->seek( nPos );
116 : 0 : }
117 : :
118 : 0 : sal_Int64 SAL_CALL OleOutputStream::getPosition() throw( IOException, RuntimeException )
119 : : {
120 : 0 : ensureSeekable();
121 : 0 : return mxSeekable->getPosition();
122 : : }
123 : :
124 : 0 : sal_Int64 SAL_CALL OleOutputStream::getLength() throw( IOException, RuntimeException )
125 : : {
126 : 0 : ensureSeekable();
127 : 0 : return mxSeekable->getLength();
128 : : }
129 : :
130 : 209 : void SAL_CALL OleOutputStream::writeBytes( const Sequence< sal_Int8 >& rData ) throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException )
131 : : {
132 : 209 : ensureConnected();
133 : 209 : mxOutStrm->writeBytes( rData );
134 : 209 : }
135 : :
136 : 209 : void SAL_CALL OleOutputStream::flush() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException )
137 : : {
138 : 209 : ensureConnected();
139 : 209 : mxOutStrm->flush();
140 : 209 : }
141 : :
142 : 209 : void SAL_CALL OleOutputStream::closeOutput() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException )
143 : : {
144 [ + - ]: 209 : ensureConnected();
145 [ + - ]: 209 : ensureSeekable();
146 : : // remember the class members
147 : 209 : Reference< XOutputStream > xOutStrm = mxOutStrm;
148 : 209 : Reference< XSeekable > xSeekable = mxSeekable;
149 : : // reset all class members
150 : 209 : mxOutStrm.clear();
151 : 209 : mxSeekable.clear();
152 : : // close stream (and let it throw something if needed)
153 [ + - ][ + - ]: 209 : xOutStrm->closeOutput();
154 : : // on success, insert the stream into the OLE storage (must be seeked back before)
155 [ + - ][ + - ]: 209 : xSeekable->seek( 0 );
156 [ + - ][ + - ]: 209 : if( !ContainerHelper::insertByName( mxStorage, maElementName, Any( mxTempFile ) ) )
[ - + ]
157 [ # # ]: 209 : throw IOException();
158 : 209 : }
159 : :
160 : 209 : void OleOutputStream::ensureSeekable() const throw( IOException )
161 : : {
162 [ - + ]: 209 : if( !mxSeekable.is() )
163 [ # # ]: 0 : throw IOException();
164 : 209 : }
165 : :
166 : 627 : void OleOutputStream::ensureConnected() const throw( NotConnectedException )
167 : : {
168 [ - + ]: 627 : if( !mxOutStrm.is() )
169 [ # # ]: 0 : throw NotConnectedException();
170 : 627 : }
171 : :
172 : : } // namespace
173 : :
174 : : // ============================================================================
175 : :
176 : 306 : OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
177 : : const Reference< XInputStream >& rxInStream, bool bBaseStreamAccess ) :
178 : : StorageBase( rxInStream, bBaseStreamAccess ),
179 : : mxContext( rxContext ),
180 : 306 : mpParentStorage( 0 )
181 : : {
182 : : OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
183 [ + - ]: 306 : initStorage( rxInStream );
184 : 306 : }
185 : :
186 : 13 : OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
187 : : const Reference< XStream >& rxOutStream, bool bBaseStreamAccess ) :
188 : : StorageBase( rxOutStream, bBaseStreamAccess ),
189 : : mxContext( rxContext ),
190 : 13 : mpParentStorage( 0 )
191 : : {
192 : : OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
193 [ + - ]: 13 : initStorage( rxOutStream );
194 : 13 : }
195 : :
196 : 29 : OleStorage::OleStorage( const OleStorage& rParentStorage,
197 : : const Reference< XNameContainer >& rxStorage, const OUString& rElementName, bool bReadOnly ) :
198 : : StorageBase( rParentStorage, rElementName, bReadOnly ),
199 : : mxContext( rParentStorage.mxContext ),
200 : : mxStorage( rxStorage ),
201 : 29 : mpParentStorage( &rParentStorage )
202 : : {
203 : : OSL_ENSURE( mxStorage.is(), "OleStorage::OleStorage - missing substorage elements" );
204 : 29 : }
205 : :
206 : 13 : OleStorage::OleStorage( const OleStorage& rParentStorage,
207 : : const Reference< XStream >& rxOutStream, const OUString& rElementName ) :
208 : : StorageBase( rParentStorage, rElementName, false ),
209 : : mxContext( rParentStorage.mxContext ),
210 : 13 : mpParentStorage( &rParentStorage )
211 : : {
212 [ + - ]: 13 : initStorage( rxOutStream );
213 : 13 : }
214 : :
215 : 361 : OleStorage::~OleStorage()
216 : : {
217 [ - + ]: 594 : }
218 : :
219 : : // ----------------------------------------------------------------------------
220 : :
221 : 306 : void OleStorage::initStorage( const Reference< XInputStream >& rxInStream )
222 : : {
223 : : // if stream is not seekable, create temporary copy
224 : 306 : Reference< XInputStream > xInStrm = rxInStream;
225 [ + + ][ + - ]: 306 : if( !Reference< XSeekable >( xInStrm, UNO_QUERY ).is() ) try
226 : : {
227 [ + - ][ + - ]: 191 : Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
[ + - ]
228 [ + - ][ + - ]: 191 : Reference< XStream > xTempFile( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.io.TempFile" ) ), UNO_QUERY_THROW );
[ + - ][ + - ]
229 : : {
230 [ + - ][ + - ]: 191 : Reference< XOutputStream > xOutStrm( xTempFile->getOutputStream(), UNO_SET_THROW );
[ + - ]
231 : : /* Pass false to both binary stream objects to keep the UNO
232 : : streams alive. Life time of these streams is controlled by the
233 : : tempfile implementation. */
234 [ + - ]: 191 : BinaryXOutputStream aOutStrm( xOutStrm, false );
235 [ + - ]: 191 : BinaryXInputStream aInStrm( xInStrm, false );
236 [ + - ][ + - ]: 191 : aInStrm.copyToStream( aOutStrm );
[ + - ]
237 : : } // scope closes output stream of tempfile
238 [ + - ][ + - ]: 191 : xInStrm = xTempFile->getInputStream();
[ # # ][ + - ]
239 : : }
240 [ # # ]: 0 : catch(const Exception& )
241 : : {
242 : : OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" );
243 : : }
244 : :
245 : : // create base storage object
246 [ + - ]: 306 : if( xInStrm.is() ) try
247 : : {
248 [ + - ][ + - ]: 306 : Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
[ + - ]
249 [ + - ]: 306 : Sequence< Any > aArgs( 2 );
250 [ + - ][ + - ]: 306 : aArgs[ 0 ] <<= xInStrm;
251 [ + - ][ + - ]: 306 : aArgs[ 1 ] <<= true; // true = do not create a copy of the input stream
252 [ + - ]: 306 : mxStorage.set( xFactory->createInstanceWithArguments(
253 [ + - ][ + - ]: 306 : CREATE_OUSTRING( "com.sun.star.embed.OLESimpleStorage" ), aArgs ), UNO_QUERY_THROW );
[ + + ][ + - ]
[ - + ]
254 : : }
255 [ + - ]: 282 : catch(const Exception& )
256 : : {
257 : 306 : }
258 : 306 : }
259 : :
260 : 26 : void OleStorage::initStorage( const Reference< XStream >& rxOutStream )
261 : : {
262 : : // create base storage object
263 [ + - ]: 26 : if( rxOutStream.is() ) try
264 : : {
265 [ + - ][ + - ]: 26 : Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
[ + - ]
266 [ + - ]: 26 : Sequence< Any > aArgs( 2 );
267 [ + - ][ + - ]: 26 : aArgs[ 0 ] <<= rxOutStream;
268 [ + - ][ + - ]: 26 : aArgs[ 1 ] <<= true; // true = do not create a copy of the stream
269 [ + - ]: 26 : mxStorage.set( xFactory->createInstanceWithArguments(
270 [ + - ][ + - ]: 26 : CREATE_OUSTRING( "com.sun.star.embed.OLESimpleStorage" ), aArgs ), UNO_QUERY_THROW );
[ + - ][ + - ]
[ # # ]
271 : : }
272 : 0 : catch(const Exception& )
273 : : {
274 : : }
275 : 26 : }
276 : :
277 : : // StorageBase interface ------------------------------------------------------
278 : :
279 : 452 : bool OleStorage::implIsStorage() const
280 : : {
281 [ + + ]: 452 : if( mxStorage.is() ) try
282 : : {
283 : : /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
284 : : implementation throws an exception. But we do not return the result
285 : : of hasElements(), because an empty storage is a valid storage too. */
286 [ + - ][ + + ]: 269 : mxStorage->hasElements();
287 : 261 : return true;
288 : : }
289 : 8 : catch(const Exception& )
290 : : {
291 : : }
292 [ - + ]: 460 : return false;
293 : : }
294 : :
295 : 0 : Reference< XStorage > OleStorage::implGetXStorage() const
296 : : {
297 : : OSL_FAIL( "OleStorage::getXStorage - not implemented" );
298 : 0 : return Reference< XStorage >();
299 : : }
300 : :
301 : 39 : void OleStorage::implGetElementNames( ::std::vector< OUString >& orElementNames ) const
302 : : {
303 [ + - ]: 39 : Sequence< OUString > aNames;
304 [ + - ]: 39 : if( mxStorage.is() ) try
305 : : {
306 [ + - ][ + - ]: 39 : aNames = mxStorage->getElementNames();
[ + - ]
[ + - # # ]
307 [ + - ]: 39 : if( aNames.getLength() > 0 )
308 [ + - ]: 39 : orElementNames.insert( orElementNames.end(), aNames.getConstArray(), aNames.getConstArray() + aNames.getLength() );
309 : : }
310 [ # # ]: 0 : catch(const Exception& )
311 : : {
312 [ + - ]: 39 : }
313 : 39 : }
314 : :
315 : 376 : StorageRef OleStorage::implOpenSubStorage( const OUString& rElementName, bool bCreateMissing )
316 : : {
317 : 376 : StorageRef xSubStorage;
318 [ + - ][ + + ]: 376 : if( mxStorage.is() && !rElementName.isEmpty() )
[ + + ]
319 : : {
320 : : try
321 : : {
322 [ + - ][ + + ]: 277 : Reference< XNameContainer > xSubElements( mxStorage->getByName( rElementName ), UNO_QUERY_THROW );
[ + + ]
323 [ + - ][ + - ]: 277 : xSubStorage.reset( new OleStorage( *this, xSubElements, rElementName, true ) );
[ - + ][ + - ]
324 : : }
325 [ + - ]: 248 : catch(const Exception& )
326 : : {
327 : : }
328 : :
329 : : /* The OLESimpleStorage API implementation seems to be buggy in the
330 : : area of writable inplace substorage (sometimes it overwrites other
331 : : unrelated streams with zero bytes). We go the save way and create a
332 : : new OLE storage based on a temporary file. All operations are
333 : : performed on this clean storage. On committing, the storage will be
334 : : completely re-inserted into the parent storage. */
335 [ + - ][ + + ]: 277 : if( !isReadOnly() && (bCreateMissing || xSubStorage.get()) ) try
[ - + ][ # # ]
[ + + ]
336 : : {
337 : : // create new storage based on a temp file
338 [ + - ][ + - ]: 13 : Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
[ + - ]
339 [ + - ][ + - ]: 13 : Reference< XStream > xTempFile( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.io.TempFile" ) ), UNO_QUERY_THROW );
[ + - ][ + - ]
340 [ + - ][ + - ]: 13 : StorageRef xTempStorage( new OleStorage( *this, xTempFile, rElementName ) );
[ + - ]
341 : : // copy existing substorage into temp storage
342 [ - + ]: 13 : if( xSubStorage.get() )
343 [ # # ]: 0 : xSubStorage->copyStorageToStorage( *xTempStorage );
344 : : // return the temp storage to caller
345 [ + - ][ + - ]: 13 : xSubStorage = xTempStorage;
[ # # ]
346 : : }
347 [ # # ]: 0 : catch(const Exception& )
348 : : {
349 : : }
350 : : }
351 : 376 : return xSubStorage;
352 : : }
353 : :
354 : 300 : Reference< XInputStream > OleStorage::implOpenInputStream( const OUString& rElementName )
355 : : {
356 : 300 : Reference< XInputStream > xInStream;
357 [ + - ]: 300 : if( mxStorage.is() ) try
358 : : {
359 [ + - ][ + - ]: 300 : xInStream.set( mxStorage->getByName( rElementName ), UNO_QUERY );
[ + - ][ # # ]
360 : : }
361 [ # # ]: 0 : catch(const Exception& )
362 : : {
363 : : }
364 : 300 : return xInStream;
365 : : }
366 : :
367 : 209 : Reference< XOutputStream > OleStorage::implOpenOutputStream( const OUString& rElementName )
368 : : {
369 : 209 : Reference< XOutputStream > xOutStream;
370 [ + - ][ + - ]: 209 : if( mxStorage.is() && !rElementName.isEmpty() )
[ + - ]
371 [ + - ][ + - ]: 209 : xOutStream.set( new OleOutputStream( mxContext, mxStorage, rElementName ) );
[ + - ]
372 : 209 : return xOutStream;
373 : : }
374 : :
375 : 26 : void OleStorage::implCommit() const
376 : : {
377 : : try
378 : : {
379 : : // commit this storage (finalizes the file this storage is based on)
380 [ + - ][ + - ]: 26 : Reference< XTransactedObject >( mxStorage, UNO_QUERY_THROW )->commit();
[ + - ]
381 : : // re-insert this storage into the parent storage
382 [ + + ]: 26 : if( mpParentStorage )
383 : : {
384 [ + - ][ + - ]: 13 : if( mpParentStorage->mxStorage->hasByName( getName() ) )
[ + - ][ - + ]
385 : : {
386 : : // replaceByName() does not work (#i109539#)
387 [ # # ][ # # ]: 0 : mpParentStorage->mxStorage->removeByName( getName() );
[ # # ]
388 [ # # ][ # # ]: 0 : Reference< XTransactedObject >( mpParentStorage->mxStorage, UNO_QUERY_THROW )->commit();
[ # # ]
389 : : }
390 [ + - ][ + - ]: 13 : mpParentStorage->mxStorage->insertByName( getName(), Any( mxStorage ) );
[ + - ][ + - ]
[ # # ]
391 : : // this requires another commit(), which will be performed by the parent storage
392 : : }
393 : : }
394 : 0 : catch(const Exception& )
395 : : {
396 : : }
397 : 26 : }
398 : :
399 : : // ============================================================================
400 : :
401 : : } // namespace ole
402 [ + - ][ + - ]: 285 : } // namespace oox
403 : :
404 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|