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
OleOutputStream(const Reference<XComponentContext> & rxContext,const Reference<XNameContainer> & rxStorage,const OUString & rElementName)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
~OleOutputStream()112 OleOutputStream::~OleOutputStream()
113 {
114 }
115
seek(sal_Int64 nPos)116 void SAL_CALL OleOutputStream::seek( sal_Int64 nPos ) throw( IllegalArgumentException, IOException, RuntimeException )
117 {
118 ensureSeekable();
119 mxSeekable->seek( nPos );
120 }
121
getPosition()122 sal_Int64 SAL_CALL OleOutputStream::getPosition() throw( IOException, RuntimeException )
123 {
124 ensureSeekable();
125 return mxSeekable->getPosition();
126 }
127
getLength()128 sal_Int64 SAL_CALL OleOutputStream::getLength() throw( IOException, RuntimeException )
129 {
130 ensureSeekable();
131 return mxSeekable->getLength();
132 }
133
writeBytes(const Sequence<sal_Int8> & rData)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
flush()140 void SAL_CALL OleOutputStream::flush() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException )
141 {
142 ensureConnected();
143 mxOutStrm->flush();
144 }
145
closeOutput()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
ensureSeekable() const164 void OleOutputStream::ensureSeekable() const throw( IOException )
165 {
166 if( !mxSeekable.is() )
167 throw IOException();
168 }
169
ensureConnected() const170 void OleOutputStream::ensureConnected() const throw( NotConnectedException )
171 {
172 if( !mxOutStrm.is() )
173 throw NotConnectedException();
174 }
175
176 } // namespace
177
178 // ============================================================================
179
OleStorage(const Reference<XComponentContext> & rxContext,const Reference<XInputStream> & rxInStream,bool bBaseStreamAccess)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
OleStorage(const Reference<XComponentContext> & rxContext,const Reference<XStream> & rxOutStream,bool bBaseStreamAccess)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
OleStorage(const OleStorage & rParentStorage,const Reference<XNameContainer> & rxStorage,const OUString & rElementName,bool bReadOnly)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
OleStorage(const OleStorage & rParentStorage,const Reference<XStream> & rxOutStream,const OUString & rElementName)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
~OleStorage()219 OleStorage::~OleStorage()
220 {
221 }
222
223 // ----------------------------------------------------------------------------
224
initStorage(const Reference<XInputStream> & rxInStream)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
initStorage(const Reference<XStream> & rxOutStream)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
implIsStorage() const283 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
implGetXStorage() const299 Reference< XStorage > OleStorage::implGetXStorage() const
300 {
301 OSL_ENSURE( false, "OleStorage::getXStorage - not implemented" );
302 return Reference< XStorage >();
303 }
304
implGetElementNames(::std::vector<OUString> & orElementNames) const305 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
implOpenSubStorage(const OUString & rElementName,bool bCreateMissing)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
implOpenInputStream(const OUString & rElementName)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
implOpenOutputStream(const OUString & rElementName)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
implCommit() const379 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