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