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