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