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{ Any(xInStrm),
231  Any(true) };// true = do not create a copy of the input stream
232  mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW );
233  }
234  catch(const Exception& )
235  {
236  }
237 }
238 
239 void OleStorage::initStorage( const Reference< XStream >& rxOutStream )
240 {
241  // create base storage object
242  if( rxOutStream.is() ) try
243  {
244  Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
245  Sequence< Any > aArgs{ Any(rxOutStream),
246  Any(true) }; // true = do not create a copy of the stream
247  mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW );
248  }
249  catch(const Exception& )
250  {
251  }
252 }
253 
254 // StorageBase interface ------------------------------------------------------
255 
256 bool OleStorage::implIsStorage() const
257 {
258  if( mxStorage.is() ) try
259  {
260  /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
261  implementation throws an exception. But we do not return the result
262  of hasElements(), because an empty storage is a valid storage too. */
263  (void)mxStorage->hasElements();
264  return true;
265  }
266  catch(const Exception& )
267  {
268  }
269  return false;
270 }
271 
272 Reference< XStorage > OleStorage::implGetXStorage() const
273 {
274  OSL_FAIL( "OleStorage::getXStorage - not implemented" );
275  return Reference< XStorage >();
276 }
277 
278 void OleStorage::implGetElementNames( ::std::vector< OUString >& orElementNames ) const
279 {
280  if( mxStorage.is() ) try
281  {
282  const Sequence<OUString> aNames = mxStorage->getElementNames();
283  if( aNames.hasElements() )
284  orElementNames.insert( orElementNames.end(), aNames.begin(), aNames.end() );
285  }
286  catch(const Exception& )
287  {
288  }
289 }
290 
291 StorageRef OleStorage::implOpenSubStorage( const OUString& rElementName, bool bCreateMissing )
292 {
293  StorageRef xSubStorage;
294  if( mxStorage.is() && !rElementName.isEmpty() )
295  {
296  try
297  {
298  Reference< XNameContainer > xSubElements( mxStorage->getByName( rElementName ), UNO_QUERY_THROW );
299  xSubStorage.reset( new OleStorage( *this, xSubElements, rElementName, true ) );
300  }
301  catch(const Exception& )
302  {
303  }
304 
305  /* The OLESimpleStorage API implementation seems to be buggy in the
306  area of writable inplace substorage (sometimes it overwrites other
307  unrelated streams with zero bytes). We go the save way and create a
308  new OLE storage based on a temporary file. All operations are
309  performed on this clean storage. On committing, the storage will be
310  completely re-inserted into the parent storage. */
311  if( !isReadOnly() && (bCreateMissing || xSubStorage) ) try
312  {
313  // create new storage based on a temp file
314  Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
315  StorageRef xTempStorage( new OleStorage( *this, xTempFile, rElementName ) );
316  // copy existing substorage into temp storage
317  if( xSubStorage )
318  xSubStorage->copyStorageToStorage( *xTempStorage );
319  // return the temp storage to caller
320  xSubStorage = xTempStorage;
321  }
322  catch(const Exception& )
323  {
324  }
325  }
326  return xSubStorage;
327 }
328 
329 Reference< XInputStream > OleStorage::implOpenInputStream( const OUString& rElementName )
330 {
331  Reference< XInputStream > xInStream;
332  if( mxStorage.is() ) try
333  {
334  xInStream.set( mxStorage->getByName( rElementName ), UNO_QUERY );
335  }
336  catch(const Exception& )
337  {
338  }
339  return xInStream;
340 }
341 
342 Reference< XOutputStream > OleStorage::implOpenOutputStream( const OUString& rElementName )
343 {
344  Reference< XOutputStream > xOutStream;
345  if( mxStorage.is() && !rElementName.isEmpty() )
346  xOutStream.set( new OleOutputStream( mxContext, mxStorage, rElementName ) );
347  return xOutStream;
348 }
349 
350 void OleStorage::implCommit() const
351 {
352  try
353  {
354  // commit this storage (finalizes the file this storage is based on)
355  Reference< XTransactedObject >( mxStorage, UNO_QUERY_THROW )->commit();
356  // re-insert this storage into the parent storage
357  if( mpParentStorage )
358  {
359  if( mpParentStorage->mxStorage->hasByName( getName() ) )
360  {
361  // replaceByName() does not work (#i109539#)
362  mpParentStorage->mxStorage->removeByName( getName() );
363  Reference< XTransactedObject >( mpParentStorage->mxStorage, UNO_QUERY_THROW )->commit();
364  }
365  mpParentStorage->mxStorage->insertByName( getName(), Any( mxStorage ) );
366  // this requires another commit(), which will be performed by the parent storage
367  }
368  }
369  catch(const Exception& )
370  {
371  }
372 }
373 
374 } // namespace oox::ole
375 
376 /* 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
bool m_bDetectedRangeSegmentation false
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo