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