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