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