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 "xolesimplestorage.hxx"
21 
22 #include <com/sun/star/embed/OLESimpleStorage.hpp>
23 #include <com/sun/star/lang/DisposedException.hpp>
24 #include <com/sun/star/lang/NoSupportException.hpp>
25 #include <com/sun/star/io/IOException.hpp>
26 #include <com/sun/star/io/XStream.hpp>
27 #include <com/sun/star/io/XInputStream.hpp>
28 #include <com/sun/star/io/XSeekable.hpp>
29 #include <com/sun/star/io/XTruncate.hpp>
30 #include <com/sun/star/io/TempFile.hpp>
31 
32 #include <comphelper/interfacecontainer2.hxx>
33 #include <comphelper/storagehelper.hxx>
34 #include <unotools/ucbstreamhelper.hxx>
35 #include <cppuhelper/exc_hlp.hxx>
36 #include <cppuhelper/supportsservice.hxx>
37 #include <sot/stg.hxx>
38 #include <sot/storinfo.hxx>
39 
40 using namespace ::com::sun::star;
41 
42 const sal_Int32 nBytesCount = 32000;
43 
44 
OLESimpleStorage(css::uno::Reference<css::uno::XComponentContext> const & xContext,css::uno::Sequence<css::uno::Any> const & aArguments)45 OLESimpleStorage::OLESimpleStorage(
46         css::uno::Reference<css::uno::XComponentContext> const & xContext,
47         css::uno::Sequence<css::uno::Any> const &aArguments)
48 : m_bDisposed( false )
49 , m_pListenersContainer( nullptr )
50 , m_xContext( xContext )
51 , m_bNoTemporaryCopy( false )
52 {
53     sal_Int32 nArgNum = aArguments.getLength();
54     if ( nArgNum < 1 || nArgNum > 2 )
55         throw lang::IllegalArgumentException(); // TODO:
56 
57     uno::Reference< io::XStream > xStream;
58     uno::Reference< io::XInputStream > xInputStream;
59     if ( !( aArguments[0] >>= xStream ) && !( aArguments[0] >>= xInputStream ) )
60         throw lang::IllegalArgumentException(); // TODO:
61 
62     if ( nArgNum == 2 )
63     {
64         if ( !( aArguments[1] >>= m_bNoTemporaryCopy ) )
65             throw lang::IllegalArgumentException(); // TODO:
66     }
67 
68     if ( m_bNoTemporaryCopy )
69     {
70         // TODO: ???
71         // If the temporary stream is not created, the original stream must be wrapped
72         // since SvStream wrapper closes the stream is owns
73         if ( xInputStream.is() )
74         {
75             // the stream must be seekable for direct access
76             uno::Reference< io::XSeekable > xSeek( xInputStream, uno::UNO_QUERY_THROW );
77             m_pStream = ::utl::UcbStreamHelper::CreateStream( xInputStream, false );
78         }
79         else if ( xStream.is() )
80         {
81             // the stream must be seekable for direct access
82             uno::Reference< io::XSeekable > xSeek( xStream, uno::UNO_QUERY_THROW );
83             m_pStream = ::utl::UcbStreamHelper::CreateStream( xStream, false );
84         }
85         else
86             throw lang::IllegalArgumentException(); // TODO:
87     }
88     else
89     {
90         uno::Reference < io::XStream > xTempFile( io::TempFile::create(m_xContext),
91                 uno::UNO_QUERY_THROW );
92         uno::Reference < io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW );
93         uno::Reference< io::XOutputStream > xTempOut = xTempFile->getOutputStream();
94         if ( !xTempOut.is() )
95             throw uno::RuntimeException();
96 
97         if ( xInputStream.is() )
98         {
99             try
100             {
101                 uno::Reference< io::XSeekable > xSeek( xInputStream, uno::UNO_QUERY_THROW );
102                 xSeek->seek( 0 );
103             }
104             catch( uno::Exception& )
105             {}
106 
107             ::comphelper::OStorageHelper::CopyInputToOutput( xInputStream, xTempOut );
108             xTempOut->closeOutput();
109             xTempSeek->seek( 0 );
110             uno::Reference< io::XInputStream > xTempInput = xTempFile->getInputStream();
111             m_pStream = ::utl::UcbStreamHelper::CreateStream( xTempInput, false );
112         }
113         else if ( xStream.is() )
114         {
115             // not sure that the storage flashes the stream on commit
116             m_xStream = xStream;
117             m_xTempStream = xTempFile;
118 
119             uno::Reference< io::XSeekable > xSeek( xStream, uno::UNO_QUERY_THROW );
120             xSeek->seek( 0 );
121             uno::Reference< io::XInputStream > xInpStream = xStream->getInputStream();
122             if ( !xInpStream.is() || !xStream->getOutputStream().is() )
123                 throw uno::RuntimeException();
124 
125             ::comphelper::OStorageHelper::CopyInputToOutput( xInpStream, xTempOut );
126             xTempOut->flush();
127             xTempSeek->seek( 0 );
128 
129             m_pStream = ::utl::UcbStreamHelper::CreateStream( xTempFile, false );
130         }
131         else
132             throw lang::IllegalArgumentException(); // TODO:
133     }
134 
135     if ( !m_pStream || m_pStream->GetError() )
136         throw io::IOException(); // TODO
137 
138     m_pStorage.reset(new Storage( *m_pStream, false ));
139 }
140 
~OLESimpleStorage()141 OLESimpleStorage::~OLESimpleStorage()
142 {
143     try {
144         osl_atomic_increment(&m_refCount);
145         dispose();
146     } catch( uno::Exception& )
147     {}
148 
149     if ( m_pListenersContainer )
150     {
151         delete m_pListenersContainer;
152         m_pListenersContainer = nullptr;
153     }
154 }
155 
UpdateOriginal_Impl()156 void OLESimpleStorage::UpdateOriginal_Impl()
157 {
158     if ( m_bNoTemporaryCopy )
159         return;
160 
161     uno::Reference< io::XSeekable > xSeek( m_xStream, uno::UNO_QUERY_THROW );
162     xSeek->seek( 0 );
163 
164     uno::Reference< io::XSeekable > xTempSeek( m_xTempStream, uno::UNO_QUERY_THROW );
165     sal_Int64 nPos = xTempSeek->getPosition();
166     xTempSeek->seek( 0 );
167 
168     uno::Reference< io::XInputStream > xTempInp = m_xTempStream->getInputStream();
169     uno::Reference< io::XOutputStream > xOutputStream = m_xStream->getOutputStream();
170     if ( !xTempInp.is() || !xOutputStream.is() )
171         throw uno::RuntimeException();
172 
173     uno::Reference< io::XTruncate > xTrunc( xOutputStream, uno::UNO_QUERY_THROW );
174     xTrunc->truncate();
175 
176     ::comphelper::OStorageHelper::CopyInputToOutput( xTempInp, xOutputStream );
177     xOutputStream->flush();
178     xTempSeek->seek( nPos );
179 }
180 
181 
InsertInputStreamToStorage_Impl(BaseStorage * pStorage,const OUString & aName,const uno::Reference<io::XInputStream> & xInputStream)182 void OLESimpleStorage::InsertInputStreamToStorage_Impl( BaseStorage* pStorage, const OUString & aName, const uno::Reference< io::XInputStream >& xInputStream )
183 {
184     if ( !pStorage || aName.isEmpty() || !xInputStream.is() )
185         throw uno::RuntimeException();
186 
187     if ( pStorage->IsContained( aName ) )
188         throw container::ElementExistException(); // TODO:
189 
190     std::unique_ptr<BaseStorageStream> pNewStream(pStorage->OpenStream( aName ));
191     if ( !pNewStream || pNewStream->GetError() || pStorage->GetError() )
192     {
193         pNewStream.reset();
194         pStorage->ResetError();
195         throw io::IOException(); // TODO
196     }
197 
198     try
199     {
200         uno::Sequence< sal_Int8 > aData( nBytesCount );
201         sal_Int32 nRead = 0;
202         do
203         {
204             nRead = xInputStream->readBytes( aData, nBytesCount );
205 
206             sal_Int32 nWritten = pNewStream->Write( aData.getArray(), nRead );
207             if ( nWritten < nRead )
208                 throw io::IOException();
209         } while( nRead == nBytesCount );
210     }
211     catch( uno::Exception& )
212     {
213         pNewStream.reset();
214         pStorage->Remove( aName );
215 
216         throw;
217     }
218 }
219 
220 
InsertNameAccessToStorage_Impl(BaseStorage * pStorage,const OUString & aName,const uno::Reference<container::XNameAccess> & xNameAccess)221 void OLESimpleStorage::InsertNameAccessToStorage_Impl( BaseStorage* pStorage, const OUString & aName, const uno::Reference< container::XNameAccess >& xNameAccess )
222 {
223     if ( !pStorage || aName.isEmpty() || !xNameAccess.is() )
224         throw uno::RuntimeException();
225 
226     if ( pStorage->IsContained( aName ) )
227         throw container::ElementExistException(); // TODO:
228 
229     std::unique_ptr<BaseStorage> pNewStorage(pStorage->OpenStorage( aName ));
230     if ( !pNewStorage || pNewStorage->GetError() || pStorage->GetError() )
231     {
232         pNewStorage.reset();
233         pStorage->ResetError();
234         throw io::IOException(); // TODO
235     }
236 
237     try
238     {
239         const uno::Sequence< OUString > aElements = xNameAccess->getElementNames();
240         for ( const auto& rElement : aElements )
241         {
242             uno::Reference< io::XInputStream > xInputStream;
243             uno::Reference< container::XNameAccess > xSubNameAccess;
244             uno::Any aAny = xNameAccess->getByName( rElement );
245             if ( aAny >>= xInputStream )
246                 InsertInputStreamToStorage_Impl( pNewStorage.get(), rElement, xInputStream );
247             else if ( aAny >>= xSubNameAccess )
248                 InsertNameAccessToStorage_Impl( pNewStorage.get(), rElement, xSubNameAccess );
249         }
250     }
251     catch( uno::Exception& )
252     {
253         pNewStorage.reset();
254         pStorage->Remove( aName );
255 
256         throw;
257     }
258 }
259 
260 
261 //  XNameContainer
262 
263 
insertByName(const OUString & aName,const uno::Any & aElement)264 void SAL_CALL OLESimpleStorage::insertByName( const OUString& aName, const uno::Any& aElement )
265 {
266     ::osl::MutexGuard aGuard( m_aMutex );
267 
268     if ( m_bDisposed )
269         throw lang::DisposedException();
270 
271     if ( !m_pStorage )
272         throw uno::RuntimeException();
273 
274     uno::Reference< io::XStream > xStream;
275     uno::Reference< io::XInputStream > xInputStream;
276     uno::Reference< container::XNameAccess > xNameAccess;
277 
278     try
279     {
280         if ( !m_bNoTemporaryCopy && !m_xStream.is() )
281             throw io::IOException(); // TODO
282 
283         if ( aElement >>= xStream )
284             xInputStream = xStream->getInputStream();
285         else if ( !( aElement >>= xInputStream ) && !( aElement >>= xNameAccess ) )
286             throw lang::IllegalArgumentException(); // TODO:
287 
288         if ( xInputStream.is() )
289             InsertInputStreamToStorage_Impl( m_pStorage.get(), aName, xInputStream );
290         else if ( xNameAccess.is() )
291             InsertNameAccessToStorage_Impl( m_pStorage.get(), aName, xNameAccess );
292         else
293             throw uno::RuntimeException();
294     }
295     catch( uno::RuntimeException& )
296     {
297         throw;
298     }
299     catch( container::ElementExistException& )
300     {
301         throw;
302     }
303     catch( const uno::Exception& )
304     {
305         css::uno::Any anyEx = cppu::getCaughtException();
306         throw lang::WrappedTargetException("Insert has failed!",
307                                             uno::Reference< uno::XInterface >(),
308                                             anyEx );
309     }
310 }
311 
312 
removeByName(const OUString & aName)313 void SAL_CALL OLESimpleStorage::removeByName( const OUString& aName )
314 {
315     ::osl::MutexGuard aGuard( m_aMutex );
316 
317     if ( m_bDisposed )
318         throw lang::DisposedException();
319 
320     if ( !m_pStorage )
321         throw uno::RuntimeException();
322 
323     if ( !m_bNoTemporaryCopy && !m_xStream.is() )
324         throw lang::WrappedTargetException(); // io::IOException(); // TODO
325 
326     if ( !m_pStorage->IsContained( aName ) )
327         throw container::NoSuchElementException(); // TODO:
328 
329     m_pStorage->Remove( aName );
330 
331     if ( m_pStorage->GetError() )
332     {
333         m_pStorage->ResetError();
334         throw lang::WrappedTargetException(); // io::IOException(); // TODO
335     }
336 }
337 
338 
replaceByName(const OUString & aName,const uno::Any & aElement)339 void SAL_CALL OLESimpleStorage::replaceByName( const OUString& aName, const uno::Any& aElement )
340 {
341     ::osl::MutexGuard aGuard( m_aMutex );
342 
343     if ( m_bDisposed )
344         throw lang::DisposedException();
345 
346     removeByName( aName );
347 
348     try
349     {
350         insertByName( aName, aElement );
351     }
352     catch( container::ElementExistException& )
353     {
354         uno::Any aCaught( ::cppu::getCaughtException() );
355 
356         throw lang::WrappedTargetException("Can't copy raw stream",
357                                             uno::Reference< uno::XInterface >(),
358                                             aCaught );
359     }
360 }
361 
362 
getByName(const OUString & aName)363 uno::Any SAL_CALL OLESimpleStorage::getByName( const OUString& aName )
364 {
365     ::osl::MutexGuard aGuard( m_aMutex );
366 
367     if ( m_bDisposed )
368         throw lang::DisposedException();
369 
370     if ( !m_pStorage )
371         throw uno::RuntimeException();
372 
373     if ( !m_pStorage->IsContained( aName ) )
374         throw container::NoSuchElementException(); // TODO:
375 
376     uno::Any aResult;
377 
378     uno::Reference< io::XStream > xTempFile = io::TempFile::create(m_xContext);
379     uno::Reference< io::XSeekable > xSeekable( xTempFile, uno::UNO_QUERY_THROW );
380     uno::Reference< io::XOutputStream > xOutputStream = xTempFile->getOutputStream();
381     uno::Reference< io::XInputStream > xInputStream = xTempFile->getInputStream();
382     if ( !xOutputStream.is() || !xInputStream.is() )
383         throw uno::RuntimeException();
384 
385     if ( m_pStorage->IsStorage( aName ) )
386     {
387         std::unique_ptr<BaseStorage> pStrg(m_pStorage->OpenStorage( aName ));
388         m_pStorage->ResetError();
389         if ( !pStrg )
390             throw lang::WrappedTargetException(); // io::IOException(); // TODO
391 
392         std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( xTempFile, false ); // do not close the original stream
393         if ( !pStream )
394             throw uno::RuntimeException();
395 
396         std::unique_ptr<BaseStorage> pNewStor(new Storage( *pStream, false ));
397         bool bSuccess = ( pStrg->CopyTo( pNewStor.get() ) && pNewStor->Commit() &&
398                           !pNewStor->GetError() && !pStrg->GetError() );
399 
400         pNewStor.reset();
401         pStrg.reset();
402         pStream.reset();
403 
404         if ( !bSuccess )
405             throw uno::RuntimeException();
406 
407         uno::Reference< container::XNameContainer > xResultNameContainer(
408             css::embed::OLESimpleStorage::createFromInputStream(m_xContext, xInputStream, true),
409             uno::UNO_QUERY_THROW );
410 
411         aResult <<= xResultNameContainer;
412     }
413     else
414     {
415         std::unique_ptr<BaseStorageStream> pStream(m_pStorage->OpenStream( aName, StreamMode::READ | StreamMode::SHARE_DENYALL | StreamMode::NOCREATE ));
416         try
417         {
418             if ( !pStream || pStream->GetError() || m_pStorage->GetError() )
419             {
420                 m_pStorage->ResetError();
421                 throw io::IOException(); // TODO
422             }
423 
424             uno::Sequence< sal_Int8 > aData( nBytesCount );
425             sal_Int32 nSize = nBytesCount;
426             sal_Int32 nRead = 0;
427             while( 0 != ( nRead = pStream->Read( aData.getArray(), nSize ) ) )
428             {
429                 if ( nRead < nSize )
430                 {
431                     nSize = nRead;
432                     aData.realloc( nSize );
433                 }
434 
435                 xOutputStream->writeBytes( aData );
436             }
437 
438             if ( pStream->GetError() )
439                 throw io::IOException(); // TODO
440 
441             xOutputStream->closeOutput();
442             xSeekable->seek( 0 );
443         }
444         catch (const uno::RuntimeException&)
445         {
446             throw;
447         }
448         catch (const uno::Exception& ex)
449         {
450             css::uno::Any anyEx = cppu::getCaughtException();
451             throw css::lang::WrappedTargetException( ex.Message,
452                     nullptr, anyEx );
453         }
454 
455         pStream.reset();
456 
457         aResult <<= xInputStream;
458     }
459 
460     return aResult;
461 }
462 
463 
getElementNames()464 uno::Sequence< OUString > SAL_CALL OLESimpleStorage::getElementNames()
465 {
466     ::osl::MutexGuard aGuard( m_aMutex );
467 
468     if ( m_bDisposed )
469         throw lang::DisposedException();
470 
471     if ( !m_pStorage )
472         throw uno::RuntimeException();
473 
474     SvStorageInfoList aList;
475     m_pStorage->FillInfoList( &aList );
476 
477     if ( m_pStorage->GetError() )
478     {
479         m_pStorage->ResetError();
480         throw uno::RuntimeException(); // TODO:
481     }
482 
483     uno::Sequence< OUString > aSeq( aList.size() );
484     for ( size_t nInd = 0; nInd < aList.size(); nInd++ )
485         aSeq[nInd] = aList[nInd].GetName();
486 
487     return aSeq;
488 }
489 
490 
hasByName(const OUString & aName)491 sal_Bool SAL_CALL OLESimpleStorage::hasByName( const OUString& aName )
492 {
493     ::osl::MutexGuard aGuard( m_aMutex );
494 
495     if ( m_bDisposed )
496         throw lang::DisposedException();
497 
498     if ( !m_pStorage )
499         throw uno::RuntimeException();
500 
501     bool bResult = m_pStorage->IsContained( aName );
502 
503     if ( m_pStorage->GetError() )
504     {
505         m_pStorage->ResetError();
506         throw uno::RuntimeException(); // TODO:
507     }
508 
509     return bResult;
510 }
511 
512 
getElementType()513 uno::Type SAL_CALL OLESimpleStorage::getElementType()
514 {
515     ::osl::MutexGuard aGuard( m_aMutex );
516 
517     if ( m_bDisposed )
518         throw lang::DisposedException();
519 
520     return cppu::UnoType<io::XInputStream>::get();
521 }
522 
523 
hasElements()524 sal_Bool SAL_CALL OLESimpleStorage::hasElements()
525 {
526     ::osl::MutexGuard aGuard( m_aMutex );
527 
528     if ( m_bDisposed )
529         throw lang::DisposedException();
530 
531     if ( !m_pStorage )
532         throw uno::RuntimeException();
533 
534     SvStorageInfoList aList;
535     m_pStorage->FillInfoList( &aList );
536 
537     if ( m_pStorage->GetError() )
538     {
539         m_pStorage->ResetError();
540         throw uno::RuntimeException(); // TODO:
541     }
542 
543     return !aList.empty();
544 }
545 
546 
547 //  XComponent
548 
549 
dispose()550 void SAL_CALL OLESimpleStorage::dispose()
551 {
552     ::osl::MutexGuard aGuard( m_aMutex );
553 
554     if ( m_bDisposed )
555         throw lang::DisposedException();
556 
557     if ( m_pListenersContainer )
558     {
559         lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
560         m_pListenersContainer->disposeAndClear( aSource );
561     }
562 
563     m_pStorage.reset();
564     m_pStream.reset();
565 
566     m_xStream.clear();
567     m_xTempStream.clear();
568 
569     m_bDisposed = true;
570 }
571 
572 
addEventListener(const uno::Reference<lang::XEventListener> & xListener)573 void SAL_CALL OLESimpleStorage::addEventListener(
574             const uno::Reference< lang::XEventListener >& xListener )
575 {
576     ::osl::MutexGuard aGuard( m_aMutex );
577 
578     if ( m_bDisposed )
579         throw lang::DisposedException();
580 
581     if ( !m_pListenersContainer )
582         m_pListenersContainer = new ::comphelper::OInterfaceContainerHelper2( m_aMutex );
583 
584     m_pListenersContainer->addInterface( xListener );
585 }
586 
587 
removeEventListener(const uno::Reference<lang::XEventListener> & xListener)588 void SAL_CALL OLESimpleStorage::removeEventListener(
589             const uno::Reference< lang::XEventListener >& xListener )
590 {
591     ::osl::MutexGuard aGuard( m_aMutex );
592 
593     if ( m_bDisposed )
594         throw lang::DisposedException();
595 
596     if ( m_pListenersContainer )
597         m_pListenersContainer->removeInterface( xListener );
598 }
599 
600 
601 //  XTransactedObject
602 
603 
commit()604 void SAL_CALL OLESimpleStorage::commit()
605 {
606     ::osl::MutexGuard aGuard( m_aMutex );
607 
608     if ( m_bDisposed )
609         throw lang::DisposedException();
610 
611     if ( !m_pStorage )
612         throw uno::RuntimeException();
613 
614     if ( !m_bNoTemporaryCopy && !m_xStream.is() )
615         throw io::IOException(); // TODO
616 
617     if ( !m_pStorage->Commit() || m_pStorage->GetError() )
618     {
619         m_pStorage->ResetError();
620         throw io::IOException(); // TODO
621     }
622 
623     UpdateOriginal_Impl();
624 }
625 
626 
revert()627 void SAL_CALL OLESimpleStorage::revert()
628 {
629     ::osl::MutexGuard aGuard( m_aMutex );
630 
631     if ( m_bDisposed )
632         throw lang::DisposedException();
633 
634     if ( !m_pStorage )
635         throw uno::RuntimeException();
636 
637     if ( !m_bNoTemporaryCopy && !m_xStream.is() )
638         throw io::IOException(); // TODO
639 
640     if ( !m_pStorage->Revert() || m_pStorage->GetError() )
641     {
642         m_pStorage->ResetError();
643         throw io::IOException(); // TODO
644     }
645 
646     UpdateOriginal_Impl();
647 }
648 
649 
650 //  XClassifiedObject
651 
652 
getClassID()653 uno::Sequence< sal_Int8 > SAL_CALL OLESimpleStorage::getClassID()
654 {
655     ::osl::MutexGuard aGuard( m_aMutex );
656 
657     if ( m_bDisposed )
658         throw lang::DisposedException();
659 
660     if ( !m_pStorage )
661         throw uno::RuntimeException();
662 
663     return m_pStorage->GetClassName().GetByteSequence();
664 }
665 
getClassName()666 OUString SAL_CALL OLESimpleStorage::getClassName()
667 {
668     return OUString();
669 }
670 
setClassInfo(const uno::Sequence<sal_Int8> &,const OUString &)671 void SAL_CALL OLESimpleStorage::setClassInfo( const uno::Sequence< sal_Int8 >& /*aClassID*/,
672                             const OUString& /*sClassName*/ )
673 {
674     throw lang::NoSupportException();
675 }
676 
677 //  XServiceInfo
getImplementationName()678 OUString SAL_CALL OLESimpleStorage::getImplementationName()
679 {
680     return "com.sun.star.comp.embed.OLESimpleStorage";
681 }
682 
supportsService(const OUString & ServiceName)683 sal_Bool SAL_CALL OLESimpleStorage::supportsService( const OUString& ServiceName )
684 {
685     return cppu::supportsService(this, ServiceName);
686 }
687 
getSupportedServiceNames()688 uno::Sequence< OUString > SAL_CALL OLESimpleStorage::getSupportedServiceNames()
689 {
690     return { "com.sun.star.embed.OLESimpleStorage" };
691 }
692 
693 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_embed_OLESimpleStorage(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const & arguments)694 com_sun_star_comp_embed_OLESimpleStorage(
695     css::uno::XComponentContext *context,
696     css::uno::Sequence<css::uno::Any> const &arguments)
697 {
698     return cppu::acquire(new OLESimpleStorage(context, arguments));
699 }
700 
701 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
702