diff options
Diffstat (limited to 'basic/source/uno/namecont.cxx')
-rw-r--r-- | basic/source/uno/namecont.cxx | 3498 |
1 files changed, 3498 insertions, 0 deletions
diff --git a/basic/source/uno/namecont.cxx b/basic/source/uno/namecont.cxx new file mode 100644 index 000000000..05a1a5b40 --- /dev/null +++ b/basic/source/uno/namecont.cxx @@ -0,0 +1,3498 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_extensions.h> +#include <config_folders.h> + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/xml/sax/SAXException.hpp> +#include <utility> +#include <vcl/svapp.hxx> +#include <o3tl/string_view.hxx> +#include <osl/mutex.hxx> +#include <vcl/errinf.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sot/storage.hxx> +#include <comphelper/getexpandeduri.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> + +#include <namecont.hxx> +#include <basic/basicmanagerrepository.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <unotools/pathoptions.hxx> +#include <svtools/sfxecode.hxx> +#include <svtools/ehdl.hxx> +#include <basic/basmgr.hxx> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/DeploymentException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/script/LibraryNotLoadedException.hpp> +#include <com/sun/star/script/vba/VBAScriptEventId.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/util/PathSubstitution.hpp> +#include <com/sun/star/deployment/ExtensionManager.hpp> +#include <comphelper/storagehelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <memory> +#include <string_view> + +namespace basic +{ + +using namespace com::sun::star::document; +using namespace com::sun::star::container; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::io; +using namespace com::sun::star::ucb; +using namespace com::sun::star::script; +using namespace com::sun::star::beans; +using namespace com::sun::star::xml::sax; +using namespace com::sun::star::util; +using namespace com::sun::star::task; +using namespace com::sun::star::embed; +using namespace com::sun::star::frame; +using namespace com::sun::star::deployment; +using namespace com::sun::star; +using namespace cppu; +using namespace osl; + +using com::sun::star::uno::Reference; + +// #i34411: Flag for error handling during migration +static bool GbMigrationSuppressErrors = false; + + +// Implementation class NameContainer + +// Methods XElementAccess +Type NameContainer::getElementType() +{ + return mType; +} + +sal_Bool NameContainer::hasElements() +{ + bool bRet = (mnElementCount > 0); + return bRet; +} + +// Methods XNameAccess +Any NameContainer::getByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + sal_Int32 iHashResult = (*aIt).second; + Any aRetAny = mValues[ iHashResult ]; + return aRetAny; +} + +Sequence< OUString > NameContainer::getElementNames() +{ + return comphelper::containerToSequence(mNames); +} + +sal_Bool NameContainer::hasByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + bool bRet = ( aIt != mHashMap.end() ); + return bRet; +} + + +// Methods XNameReplace +void NameContainer::replaceByName( const OUString& aName, const Any& aElement ) +{ + const Type& aAnyType = aElement.getValueType(); + if( mType != aAnyType ) + { + throw IllegalArgumentException("types do not match", static_cast<cppu::OWeakObject*>(this), 2); + } + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + throw NoSuchElementException(); + } + sal_Int32 iHashResult = (*aIt).second; + Any aOldElement = mValues[ iHashResult ]; + mValues[ iHashResult ] = aElement; + + + // Fire event + if( maContainerListeners.getLength() > 0 ) + { + ContainerEvent aEvent; + aEvent.Source = mpxEventSource; + aEvent.Accessor <<= aName; + aEvent.Element = aElement; + aEvent.ReplacedElement = aOldElement; + maContainerListeners.notifyEach( &XContainerListener::elementReplaced, aEvent ); + } + + /* After the container event has been fired (one listener will update the + core Basic manager), fire change event. Listeners can rely on that the + Basic source code of the core Basic manager is up-to-date. */ + if( maChangesListeners.getLength() > 0 ) + { + ChangesEvent aEvent; + aEvent.Source = mpxEventSource; + aEvent.Base <<= aEvent.Source; + aEvent.Changes = { { Any(aName), aElement, aOldElement } }; + maChangesListeners.notifyEach( &XChangesListener::changesOccurred, aEvent ); + } +} + +void NameContainer::insertCheck(const OUString& aName, const Any& aElement) +{ + NameContainerNameMap::iterator aIt = mHashMap.find(aName); + if( aIt != mHashMap.end() ) + { + throw ElementExistException(); + } + insertNoCheck(aName, aElement); +} + +void NameContainer::insertNoCheck(const OUString& aName, const Any& aElement) +{ + const Type& aAnyType = aElement.getValueType(); + if( mType != aAnyType ) + { + throw IllegalArgumentException("types do not match", static_cast<cppu::OWeakObject*>(this), 2); + } + + sal_Int32 nCount = mNames.size(); + mNames.push_back( aName ); + mValues.push_back( aElement ); + + mHashMap[ aName ] = nCount; + mnElementCount++; + + // Fire event + if( maContainerListeners.getLength() > 0 ) + { + ContainerEvent aEvent; + aEvent.Source = mpxEventSource; + aEvent.Accessor <<= aName; + aEvent.Element = aElement; + maContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent ); + } + + /* After the container event has been fired (one listener will update the + core Basic manager), fire change event. Listeners can rely on that the + Basic source code of the core Basic manager is up-to-date. */ + if( maChangesListeners.getLength() > 0 ) + { + ChangesEvent aEvent; + aEvent.Source = mpxEventSource; + aEvent.Base <<= aEvent.Source; + aEvent.Changes = { { Any(aName), aElement, {} } }; + maChangesListeners.notifyEach( &XChangesListener::changesOccurred, aEvent ); + } +} + +// Methods XNameContainer +void NameContainer::insertByName( const OUString& aName, const Any& aElement ) +{ + insertCheck(aName, aElement); +} + +void NameContainer::removeByName( const OUString& aName ) +{ + NameContainerNameMap::iterator aIt = mHashMap.find( aName ); + if( aIt == mHashMap.end() ) + { + OUString sMessage = "\"" + aName + "\" not found"; + throw NoSuchElementException(sMessage); + } + + sal_Int32 iHashResult = (*aIt).second; + Any aOldElement = mValues[ iHashResult ]; + mHashMap.erase( aIt ); + sal_Int32 iLast = mNames.size() - 1; + if( iLast != iHashResult ) + { + mNames[ iHashResult ] = mNames[ iLast ]; + mValues[ iHashResult ] = mValues[ iLast ]; + mHashMap[ mNames[ iHashResult ] ] = iHashResult; + } + mNames.resize( iLast ); + mValues.resize( iLast ); + mnElementCount--; + + // Fire event + if( maContainerListeners.getLength() > 0 ) + { + ContainerEvent aEvent; + aEvent.Source = mpxEventSource; + aEvent.Accessor <<= aName; + aEvent.Element = aOldElement; + maContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvent ); + } + + /* After the container event has been fired (one listener will update the + core Basic manager), fire change event. Listeners can rely on that the + Basic source code of the core Basic manager is up-to-date. */ + if( maChangesListeners.getLength() > 0 ) + { + ChangesEvent aEvent; + aEvent.Source = mpxEventSource; + aEvent.Base <<= aEvent.Source; + aEvent.Changes = { { Any(aName), + {}, // Element remains empty (meaning "replaced with nothing") + aOldElement } }; + maChangesListeners.notifyEach( &XChangesListener::changesOccurred, aEvent ); + } +} + + +// Methods XContainer +void SAL_CALL NameContainer::addContainerListener( const Reference< XContainerListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("addContainerListener called with null xListener",static_cast< cppu::OWeakObject * >(this)); + } + maContainerListeners.addInterface( xListener ); +} + +void SAL_CALL NameContainer::removeContainerListener( const Reference< XContainerListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("removeContainerListener called with null xListener",static_cast< cppu::OWeakObject * >(this)); + } + maContainerListeners.removeInterface( xListener ); +} + +// Methods XChangesNotifier +void SAL_CALL NameContainer::addChangesListener( const Reference< XChangesListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("addChangesListener called with null xListener",static_cast< cppu::OWeakObject * >(this)); + } + maChangesListeners.addInterface( xListener ); +} + +void SAL_CALL NameContainer::removeChangesListener( const Reference< XChangesListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("removeChangesListener called with null xListener",static_cast< cppu::OWeakObject * >(this)); + } + maChangesListeners.removeInterface( xListener ); +} + + +// ModifiableHelper + +void ModifiableHelper::setModified( bool _bModified ) +{ + if ( _bModified == mbModified ) + { + return; + } + mbModified = _bModified; + + if ( m_aModifyListeners.getLength() == 0 ) + { + return; + } + EventObject aModifyEvent( m_rEventSource ); + m_aModifyListeners.notifyEach( &XModifyListener::modified, aModifyEvent ); +} + + +// Ctor +SfxLibraryContainer::SfxLibraryContainer() + : SfxLibraryContainer_BASE( m_aMutex ) + , maVBAScriptListeners( m_aMutex ) + , mnRunningVBAScripts( 0 ) + , mbVBACompat( false ) + , meVBATextEncoding( RTL_TEXTENCODING_DONTKNOW ) + , maModifiable( *this, m_aMutex ) + , maNameContainer( new NameContainer(cppu::UnoType<XNameAccess>::get()) ) + , mbOldInfoFormat( false ) + , mbOasis2OOoFormat( false ) + , mpBasMgr( nullptr ) + , mbOwnBasMgr( false ) + , meInitMode(DEFAULT) +{ + mxContext = comphelper::getProcessComponentContext(); + + mxSFI = ucb::SimpleFileAccess::create( mxContext ); + + mxStringSubstitution = util::PathSubstitution::create( mxContext ); +} + +SfxLibraryContainer::~SfxLibraryContainer() +{ + if( mbOwnBasMgr ) + { + delete mpBasMgr; + } +} + +void SfxLibraryContainer::enterMethod() +{ + Application::GetSolarMutex().acquire(); + if ( rBHelper.bInDispose || rBHelper.bDisposed ) + { + throw DisposedException( OUString(), *this ); + } +} + +void SfxLibraryContainer::leaveMethod() +{ + Application::GetSolarMutex().release(); +} + +BasicManager* SfxLibraryContainer::getBasicManager() +{ + try + { + if ( mpBasMgr ) + { + return mpBasMgr; + } + Reference< XModel > xDocument( mxOwnerDocument.get(), UNO_QUERY ); + SAL_WARN_IF( + !xDocument.is(), "basic", + ("SfxLibraryContainer::getBasicManager: cannot obtain a BasicManager" + " without document!")); + if ( xDocument.is() ) + { + mpBasMgr = BasicManagerRepository::getDocumentBasicManager( xDocument ); + } + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION( "basic", "SfxLibraryContainer::getBasicManager:" ); + } + return mpBasMgr; +} + +// Methods XStorageBasedLibraryContainer +Reference< XStorage > SAL_CALL SfxLibraryContainer::getRootStorage() +{ + LibraryContainerMethodGuard aGuard( *this ); + return mxStorage; +} + +void SAL_CALL SfxLibraryContainer::setRootStorage( const Reference< XStorage >& _rxRootStorage ) +{ + LibraryContainerMethodGuard aGuard( *this ); + if ( !_rxRootStorage.is() ) + { + throw IllegalArgumentException("no root storage", static_cast<cppu::OWeakObject*>(this), 1); + } + mxStorage = _rxRootStorage; + onNewRootStorage(); +} + +void SAL_CALL SfxLibraryContainer::storeLibrariesToStorage( const Reference< XStorage >& _rxRootStorage ) +{ + LibraryContainerMethodGuard aGuard( *this ); + if ( !_rxRootStorage.is() ) + { + throw IllegalArgumentException("no root storage", static_cast<cppu::OWeakObject*>(this), 1); + } + try + { + storeLibraries_Impl( _rxRootStorage, true ); + } + catch( const Exception& ) + { + throw WrappedTargetException( OUString(), + *this, ::cppu::getCaughtException() ); + } +} + + +// Methods XModifiable +sal_Bool SfxLibraryContainer::isModified() +{ + LibraryContainerMethodGuard aGuard( *this ); + if ( maModifiable.isModified() ) + { + return true; + } + // the library container is not modified, go through the libraries and check whether they are modified + Sequence< OUString > aNames = maNameContainer->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nNameCount = aNames.getLength(); + + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aName = pNames[ i ]; + try + { + SfxLibrary* pImplLib = getImplLib( aName ); + if( pImplLib->isModified() ) + { + if ( aName == "Standard" ) + { + // this is a workaround that has to be implemented because + // empty standard library should stay marked as modified + // but should not be treated as modified while it is empty + if ( pImplLib->hasElements() ) + return true; + } + else + { + return true; + } + } + } + catch(const css::container::NoSuchElementException&) + { + } + } + + return false; +} + +void SAL_CALL SfxLibraryContainer::setModified( sal_Bool _bModified ) +{ + LibraryContainerMethodGuard aGuard( *this ); + maModifiable.setModified( _bModified ); +} + +void SAL_CALL SfxLibraryContainer::addModifyListener( const Reference< XModifyListener >& _rxListener ) +{ + LibraryContainerMethodGuard aGuard( *this ); + maModifiable.addModifyListener( _rxListener ); +} + +void SAL_CALL SfxLibraryContainer::removeModifyListener( const Reference< XModifyListener >& _rxListener ) +{ + LibraryContainerMethodGuard aGuard( *this ); + maModifiable.removeModifyListener( _rxListener ); +} + +// Methods XPersistentLibraryContainer +Any SAL_CALL SfxLibraryContainer::getRootLocation() +{ + LibraryContainerMethodGuard aGuard( *this ); + return Any( getRootStorage() ); +} + +OUString SAL_CALL SfxLibraryContainer::getContainerLocationName() +{ + LibraryContainerMethodGuard aGuard( *this ); + return maLibrariesDir; +} + +void SAL_CALL SfxLibraryContainer::storeLibraries( ) +{ + LibraryContainerMethodGuard aGuard( *this ); + try + { + storeLibraries_Impl( mxStorage, mxStorage.is() ); + // we need to store *all* libraries if and only if we are based on a storage: + // in this case, storeLibraries_Impl will remove the source storage, after loading + // all libraries, so we need to force them to be stored, again + } + catch( const Exception& ) + { + throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() ); + } +} + +static void checkAndCopyFileImpl( const INetURLObject& rSourceFolderInetObj, + const INetURLObject& rTargetFolderInetObj, + std::u16string_view rCheckFileName, + std::u16string_view rCheckExtension, + const Reference< XSimpleFileAccess3 >& xSFI ) +{ + INetURLObject aTargetFolderInetObj( rTargetFolderInetObj ); + aTargetFolderInetObj.insertName( rCheckFileName, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aTargetFolderInetObj.setExtension( rCheckExtension ); + OUString aTargetFile = aTargetFolderInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( !xSFI->exists( aTargetFile ) ) + { + INetURLObject aSourceFolderInetObj( rSourceFolderInetObj ); + aSourceFolderInetObj.insertName( rCheckFileName, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aSourceFolderInetObj.setExtension( rCheckExtension ); + OUString aSourceFile = aSourceFolderInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + xSFI->copy( aSourceFile, aTargetFile ); + } +} + +static void createVariableURL( OUString& rStr, std::u16string_view rLibName, + std::u16string_view rInfoFileName, bool bUser ) +{ + if( bUser ) + { + rStr = "$(USER)/basic/"; + } + else + { + rStr = "$(INST)/" LIBO_SHARE_FOLDER "/basic/"; + } + rStr += OUString::Concat(rLibName) + "/" + rInfoFileName + ".xlb/"; +} + +void SfxLibraryContainer::init( const OUString& rInitialDocumentURL, const uno::Reference< embed::XStorage >& rxInitialStorage ) +{ + // this might be called from within the ctor, and the impl_init might (indirectly) create + // a UNO reference to ourself. + // Ensure that we're not destroyed while we're in here + osl_atomic_increment( &m_refCount ); + init_Impl( rInitialDocumentURL, rxInitialStorage ); + osl_atomic_decrement( &m_refCount ); +} + +void SfxLibraryContainer::init_Impl( const OUString& rInitialDocumentURL, + const uno::Reference< embed::XStorage >& rxInitialStorage ) +{ + uno::Reference< embed::XStorage > xStorage = rxInitialStorage; + + maInitialDocumentURL = rInitialDocumentURL; + maInfoFileName = getInfoFileName(); + maOldInfoFileName = getOldInfoFileName(); + maLibElementFileExtension = getLibElementFileExtension(); + maLibrariesDir = getLibrariesDir(); + + meInitMode = DEFAULT; + INetURLObject aInitUrlInetObj( maInitialDocumentURL ); + OUString aInitFileName = aInitUrlInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( !aInitFileName.isEmpty() ) + { + // We need a BasicManager to avoid problems + StarBASIC* pBas = new StarBASIC(); + mpBasMgr = new BasicManager( pBas ); + mbOwnBasMgr = true; + + OUString aExtension = aInitUrlInetObj.getExtension(); + if( aExtension == "xlc" ) + { + meInitMode = CONTAINER_INIT_FILE; + INetURLObject aLibPathInetObj( aInitUrlInetObj ); + aLibPathInetObj.removeSegment(); + maLibraryPath = aLibPathInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + else if( aExtension == "xlb" ) + { + meInitMode = LIBRARY_INIT_FILE; + uno::Reference< embed::XStorage > xDummyStor; + ::xmlscript::LibDescriptor aLibDesc; + implLoadLibraryIndexFile( nullptr, aLibDesc, xDummyStor, aInitFileName ); + return; + } + else + { + // Decide between old and new document + bool bOldStorage = SotStorage::IsOLEStorage( aInitFileName ); + if ( bOldStorage ) + { + meInitMode = OLD_BASIC_STORAGE; + importFromOldStorage( aInitFileName ); + return; + } + else + { + meInitMode = OFFICE_DOCUMENT; + try + { + xStorage = ::comphelper::OStorageHelper::GetStorageFromURL( aInitFileName, embed::ElementModes::READ ); + } + catch (const uno::Exception& ) + { + // TODO: error handling + } + } + } + } + else + { + // Default paths + maLibraryPath = SvtPathOptions().GetBasicPath(); + } + + uno::Reference< io::XInputStream > xInput; + + mxStorage = xStorage; + bool bStorage = mxStorage.is(); + + + // #110009: Scope to force the StorageRefs to be destructed and + // so the streams to be closed before the preload operation + { + + uno::Reference< embed::XStorage > xLibrariesStor; + OUString aFileName; + + int nPassCount = 1; + if( !bStorage && meInitMode == DEFAULT ) + { + nPassCount = 2; + } + for( int nPass = 0 ; nPass < nPassCount ; nPass++ ) + { + if( bStorage ) + { + SAL_WARN_IF( + meInitMode != DEFAULT && meInitMode != OFFICE_DOCUMENT, "basic", + "Wrong InitMode for document"); + try + { + uno::Reference< io::XStream > xStream; + xLibrariesStor = xStorage->openStorageElement( maLibrariesDir, embed::ElementModes::READ ); + + if ( xLibrariesStor.is() ) + { + aFileName = maInfoFileName + "-lc.xml"; + try + { + xStream = xLibrariesStor->openStreamElement( aFileName, embed::ElementModes::READ ); + } + catch(const uno::Exception& ) + {} + + if( !xStream.is() ) + { + mbOldInfoFormat = true; + + // Check old version + aFileName = maOldInfoFileName + ".xml"; + try + { + xStream = xLibrariesStor->openStreamElement( aFileName, embed::ElementModes::READ ); + } + catch(const uno::Exception& ) + {} + + if( !xStream.is() ) + { + // Check for EA2 document version with wrong extensions + aFileName = maOldInfoFileName + ".xli"; + xStream = xLibrariesStor->openStreamElement( aFileName, embed::ElementModes::READ ); + } + } + } + + if ( xStream.is() ) + { + xInput = xStream->getInputStream(); + } + } + catch(const uno::Exception& ) + { + // TODO: error handling? + } + } + else + { + std::unique_ptr<INetURLObject> pLibInfoInetObj; + if( meInitMode == CONTAINER_INIT_FILE ) + { + aFileName = aInitFileName; + } + else + { + if( nPass == 1 ) + { + pLibInfoInetObj.reset(new INetURLObject( o3tl::getToken(maLibraryPath, 0, ';') )); + } + else + { + pLibInfoInetObj.reset(new INetURLObject( o3tl::getToken(maLibraryPath, 1, ';') )); + } + pLibInfoInetObj->insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + pLibInfoInetObj->setExtension( u"xlc" ); + aFileName = pLibInfoInetObj->GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + try + { + xInput = mxSFI->openFileRead( aFileName ); + } + catch(const Exception& ) + { + // Silently tolerate empty or missing files + xInput.clear(); + } + + // Old variant? + if( !xInput.is() && nPass == 0 ) + { + INetURLObject aLibInfoInetObj( o3tl::getToken(maLibraryPath, 1, ';') ); + aLibInfoInetObj.insertName( maOldInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aLibInfoInetObj.setExtension( u"xli" ); + aFileName = aLibInfoInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + try + { + xInput = mxSFI->openFileRead( aFileName ); + mbOldInfoFormat = true; + } + catch(const Exception& ) + { + xInput.clear(); + } + } + } + + if( xInput.is() ) + { + InputSource source; + source.aInputStream = xInput; + source.sSystemId = aFileName; + + // start parsing + auto pLibArray = std::make_unique<::xmlscript::LibDescriptorArray> ( ); + + Reference< XParser > xParser = xml::sax::Parser::create(mxContext); + try + { + xParser->setDocumentHandler( ::xmlscript::importLibraryContainer( pLibArray.get() ) ); + xParser->parseStream( source ); + } + catch ( const xml::sax::SAXException& ) + { + TOOLS_WARN_EXCEPTION( "basic", "" ); + return; + } + catch ( const io::IOException& ) + { + TOOLS_WARN_EXCEPTION( "basic", "" ); + return; + } + + sal_Int32 nLibCount = pLibArray->mnLibCount; + for( sal_Int32 i = 0 ; i < nLibCount ; i++ ) + { + ::xmlscript::LibDescriptor& rLib = pLibArray->mpLibs[i]; + + // Check storage URL + OUString aStorageURL = rLib.aStorageURL; + if( !bStorage && aStorageURL.isEmpty() && nPass == 0 ) + { + OUString aLibraryPath; + if( meInitMode == CONTAINER_INIT_FILE ) + { + aLibraryPath = maLibraryPath; + } + else + { + aLibraryPath = maLibraryPath.getToken(1, ';'); + } + INetURLObject aInetObj( aLibraryPath ); + + aInetObj.insertName( rLib.aName, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + OUString aLibDirPath = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( mxSFI->isFolder( aLibDirPath ) ) + { + createVariableURL( rLib.aStorageURL, rLib.aName, maInfoFileName, true ); + maModifiable.setModified( true ); + } + else if( rLib.bLink ) + { + // Check "share" path + INetURLObject aShareInetObj( o3tl::getToken(maLibraryPath, 0, ';') ); + aShareInetObj.insertName( rLib.aName, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + OUString aShareLibDirPath = aShareInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( mxSFI->isFolder( aShareLibDirPath ) ) + { + createVariableURL( rLib.aStorageURL, rLib.aName, maInfoFileName, false ); + maModifiable.setModified( true ); + } + else + { + // #i25537: Ignore lib if library folder does not really exist + continue; + } + } + } + + OUString aLibName = rLib.aName; + + // If the same library name is used by the shared and the + // user lib container index files the user file wins + if( nPass == 1 && hasByName( aLibName ) ) + { + continue; + } + SfxLibrary* pImplLib; + if( rLib.bLink ) + { + Reference< XNameAccess > xLib = + createLibraryLink( aLibName, rLib.aStorageURL, rLib.bReadOnly ); + pImplLib = static_cast< SfxLibrary* >( xLib.get() ); + } + else + { + Reference< XNameContainer > xLib = createLibrary( aLibName ); + pImplLib = static_cast< SfxLibrary* >( xLib.get() ); + pImplLib->mbLoaded = false; + pImplLib->mbReadOnly = rLib.bReadOnly; + if( !bStorage ) + { + checkStorageURL( rLib.aStorageURL, pImplLib->maLibInfoFileURL, + pImplLib->maStorageURL, pImplLib->maUnexpandedStorageURL ); + } + } + maModifiable.setModified( false ); + + // Read library info files + if( !mbOldInfoFormat ) + { + uno::Reference< embed::XStorage > xLibraryStor; + if( !pImplLib->mbInitialised && bStorage ) + { + try + { + xLibraryStor = xLibrariesStor->openStorageElement( rLib.aName, + embed::ElementModes::READ ); + } + catch(const uno::Exception& ) + { + #if OSL_DEBUG_LEVEL > 0 + TOOLS_WARN_EXCEPTION( + "basic", + "couldn't open sub storage for library \"" << rLib.aName << "\""); + #endif + } + } + + // Link is already initialised in createLibraryLink() + if( !pImplLib->mbInitialised && (!bStorage || xLibraryStor.is()) ) + { + bool bLoaded = implLoadLibraryIndexFile( pImplLib, rLib, xLibraryStor, OUString() ); + SAL_WARN_IF( + bLoaded && aLibName != rLib.aName, "basic", + ("Different library names in library container and" + " library info files!")); + if( GbMigrationSuppressErrors && !bLoaded ) + { + removeLibrary( aLibName ); + } + } + } + else if( !bStorage ) + { + // Write new index file immediately because otherwise + // the library elements will be lost when storing into + // the new info format + uno::Reference< embed::XStorage > xTmpStorage; + implStoreLibraryIndexFile( pImplLib, rLib, xTmpStorage ); + } + + implImportLibDescriptor( pImplLib, rLib ); + + if( nPass == 1 ) + { + pImplLib->mbSharedIndexFile = true; + pImplLib->mbReadOnly = true; + } + } + + // Keep flag for documents to force writing the new index files + if( !bStorage ) + { + mbOldInfoFormat = false; + } + } + } + + // #110009: END Scope to force the StorageRefs to be destructed + } + + if( !bStorage && meInitMode == DEFAULT ) + { + try + { + implScanExtensions(); + } + catch(const uno::Exception& ) + { + // TODO: error handling? + SAL_WARN("basic", "Cannot access extensions!"); + } + } + + // Preload? + { + Sequence< OUString > aNames = maNameContainer->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nNameCount = aNames.getLength(); + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aName = pNames[ i ]; + SfxLibrary* pImplLib = getImplLib( aName ); + if( pImplLib->mbPreload ) + { + loadLibrary( aName ); + } + } + } + + if( meInitMode != DEFAULT ) + return; + + // tdf#121740 speed up loading documents with lots of embedded documents by avoid the UCB work of updating non-existent VBA libraries + if (rInitialDocumentURL.isEmpty()) + return; + + INetURLObject aUserBasicInetObj( o3tl::getToken(maLibraryPath, 1, ';') ); + OUString aStandardStr("Standard"); + + INetURLObject aPrevUserBasicInetObj_1( aUserBasicInetObj ); + aPrevUserBasicInetObj_1.removeSegment(); + INetURLObject aPrevUserBasicInetObj_2 = aPrevUserBasicInetObj_1; + aPrevUserBasicInetObj_1.Append( u"__basic_80" ); + aPrevUserBasicInetObj_2.Append( u"__basic_80_2" ); + + // #i93163 + bool bCleanUp = false; + try + { + INetURLObject aPrevUserBasicInetObj = aPrevUserBasicInetObj_1; + OUString aPrevFolder = aPrevUserBasicInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( mxSFI->isFolder( aPrevFolder ) ) + { + // Check if Standard folder exists and is complete + INetURLObject aUserBasicStandardInetObj( aUserBasicInetObj ); + aUserBasicStandardInetObj.insertName( aStandardStr, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + INetURLObject aPrevUserBasicStandardInetObj( aPrevUserBasicInetObj ); + aPrevUserBasicStandardInetObj.insertName( aStandardStr, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + OUString aPrevStandardFolder = aPrevUserBasicStandardInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( mxSFI->isFolder( aPrevStandardFolder ) ) + { + OUString aXlbExtension( "xlb" ); + OUString aCheckFileName; + + // Check if script.xlb exists + aCheckFileName = "script"; + checkAndCopyFileImpl( aUserBasicStandardInetObj, + aPrevUserBasicStandardInetObj, + aCheckFileName, aXlbExtension, mxSFI ); + + // Check if dialog.xlb exists + aCheckFileName = "dialog"; + checkAndCopyFileImpl( aUserBasicStandardInetObj, + aPrevUserBasicStandardInetObj, + aCheckFileName, aXlbExtension, mxSFI ); + + // Check if module1.xba exists + aCheckFileName = "Module1"; + checkAndCopyFileImpl( aUserBasicStandardInetObj, + aPrevUserBasicStandardInetObj, + aCheckFileName, u"xba", mxSFI ); + } + else + { + OUString aStandardFolder = aUserBasicStandardInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + mxSFI->copy( aStandardFolder, aPrevStandardFolder ); + } + + OUString aPrevCopyToFolder = aPrevUserBasicInetObj_2.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + mxSFI->copy( aPrevFolder, aPrevCopyToFolder ); + } + else + { + aPrevUserBasicInetObj = aPrevUserBasicInetObj_2; + aPrevFolder = aPrevUserBasicInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + if( mxSFI->isFolder( aPrevFolder ) ) + { + rtl::Reference<SfxLibraryContainer> pPrevCont = createInstanceImpl(); + + // Rename previous basic folder to make storage URLs correct during initialisation + OUString aFolderUserBasic = aUserBasicInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + INetURLObject aUserBasicTmpInetObj( aUserBasicInetObj ); + aUserBasicTmpInetObj.removeSegment(); + aUserBasicTmpInetObj.Append( u"__basic_tmp" ); + OUString aFolderTmp = aUserBasicTmpInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + mxSFI->move( aFolderUserBasic, aFolderTmp ); + try + { + mxSFI->move( aPrevFolder, aFolderUserBasic ); + } + catch(const Exception& ) + { + // Move back user/basic folder + try + { + mxSFI->kill( aFolderUserBasic ); + } + catch(const Exception& ) + {} + mxSFI->move( aFolderTmp, aFolderUserBasic ); + throw; + } + + INetURLObject aPrevUserBasicLibInfoInetObj( aUserBasicInetObj ); + aPrevUserBasicLibInfoInetObj.insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aPrevUserBasicLibInfoInetObj.setExtension( u"xlc"); + OUString aLibInfoFileName = aPrevUserBasicLibInfoInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + Sequence<Any> aInitSeq( 1 ); + aInitSeq.getArray()[0] <<= aLibInfoFileName; + GbMigrationSuppressErrors = true; + pPrevCont->initialize( aInitSeq ); + GbMigrationSuppressErrors = false; + + // Rename folders back + mxSFI->move( aFolderUserBasic, aPrevFolder ); + mxSFI->move( aFolderTmp, aFolderUserBasic ); + + Sequence< OUString > aNames = pPrevCont->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nNameCount = aNames.getLength(); + + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aLibName = pNames[ i ]; + if( hasByName( aLibName ) ) + { + if( aLibName == aStandardStr ) + { + SfxLibrary* pImplLib = getImplLib( aStandardStr ); + OUString aStandardFolder = pImplLib->maStorageURL; + mxSFI->kill( aStandardFolder ); + } + else + { + continue; + } + } + + SfxLibrary* pImplLib = pPrevCont->getImplLib( aLibName ); + if( pImplLib->mbLink ) + { + OUString aStorageURL = pImplLib->maUnexpandedStorageURL; + bool bCreateLink = true; + if( aStorageURL.indexOf( "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE" ) != -1 || + aStorageURL.indexOf( "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE" ) != -1 || + aStorageURL.indexOf( "vnd.sun.star.expand:$BUNDLED_EXTENSIONS" ) != -1 || + aStorageURL.indexOf( "$(INST)" ) != -1 ) + { + bCreateLink = false; + } + if( bCreateLink ) + { + createLibraryLink( aLibName, pImplLib->maStorageURL, pImplLib->mbReadOnly ); + } + } + else + { + // Move folder if not already done + INetURLObject aUserBasicLibFolderInetObj( aUserBasicInetObj ); + aUserBasicLibFolderInetObj.Append( aLibName ); + OUString aLibFolder = aUserBasicLibFolderInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + INetURLObject aPrevUserBasicLibFolderInetObj( aPrevUserBasicInetObj ); + aPrevUserBasicLibFolderInetObj.Append( aLibName ); + OUString aPrevLibFolder = aPrevUserBasicLibFolderInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( mxSFI->isFolder( aPrevLibFolder ) && !mxSFI->isFolder( aLibFolder ) ) + { + mxSFI->move( aPrevLibFolder, aLibFolder ); + } + + if( aLibName == aStandardStr ) + { + maNameContainer->removeByName( aLibName ); + } + + // Create library + Reference< XNameContainer > xLib = createLibrary( aLibName ); + SfxLibrary* pNewLib = static_cast< SfxLibrary* >( xLib.get() ); + pNewLib->mbLoaded = false; + pNewLib->implSetModified( false ); + checkStorageURL( aLibFolder, pNewLib->maLibInfoFileURL, + pNewLib->maStorageURL, pNewLib->maUnexpandedStorageURL ); + + uno::Reference< embed::XStorage > xDummyStor; + ::xmlscript::LibDescriptor aLibDesc; + implLoadLibraryIndexFile( pNewLib, aLibDesc, xDummyStor, pNewLib->maLibInfoFileURL ); + implImportLibDescriptor( pNewLib, aLibDesc ); + } + } + mxSFI->kill( aPrevFolder ); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("basic", "Upgrade of Basic installation failed somehow" ); + bCleanUp = true; + } + + // #i93163 + if( !bCleanUp ) + return; + + INetURLObject aPrevUserBasicInetObj_Err( aUserBasicInetObj ); + aPrevUserBasicInetObj_Err.removeSegment(); + aPrevUserBasicInetObj_Err.Append( u"__basic_80_err" ); + OUString aPrevFolder_Err = aPrevUserBasicInetObj_Err.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + bool bSaved = false; + try + { + OUString aPrevFolder_1 = aPrevUserBasicInetObj_1.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( mxSFI->isFolder( aPrevFolder_1 ) ) + { + mxSFI->move( aPrevFolder_1, aPrevFolder_Err ); + bSaved = true; + } + } + catch(const Exception& ) + {} + try + { + OUString aPrevFolder_2 = aPrevUserBasicInetObj_2.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( !bSaved && mxSFI->isFolder( aPrevFolder_2 ) ) + { + mxSFI->move( aPrevFolder_2, aPrevFolder_Err ); + } + else + { + mxSFI->kill( aPrevFolder_2 ); + } + } + catch(const Exception& ) + {} +} + +void SfxLibraryContainer::implScanExtensions() +{ +#if HAVE_FEATURE_EXTENSIONS + ScriptExtensionIterator aScriptIt; + + bool bPureDialogLib = false; + for (;;) + { + OUString aLibURL = aScriptIt.nextBasicOrDialogLibrary( bPureDialogLib ); + if (aLibURL.isEmpty()) + break; + if( bPureDialogLib && maInfoFileName == "script" ) + { + continue; + } + // Extract lib name + sal_Int32 nLen = aLibURL.getLength(); + sal_Int32 indexLastSlash = aLibURL.lastIndexOf( '/' ); + sal_Int32 nReduceCopy = 0; + if( indexLastSlash == nLen - 1 ) + { + nReduceCopy = 1; + indexLastSlash = aLibURL.lastIndexOf( '/', nLen - 1 ); + } + + OUString aLibName = aLibURL.copy( indexLastSlash + 1, nLen - indexLastSlash - nReduceCopy - 1 ); + + // If a library of the same exists the existing library wins + if( hasByName( aLibName ) ) + { + continue; + } + // Add index file to URL + OUString aIndexFileURL = aLibURL; + if( nReduceCopy == 0 ) + { + aIndexFileURL += "/"; + } + aIndexFileURL += maInfoFileName + ".xlb"; + + // Create link + const bool bReadOnly = false; + createLibraryLink( aLibName, aIndexFileURL, bReadOnly ); + } +#else + (void) this; +#endif +} + +// Handle maLibInfoFileURL and maStorageURL correctly +void SfxLibraryContainer::checkStorageURL( const OUString& aSourceURL, + OUString& aLibInfoFileURL, OUString& aStorageURL, + OUString& aUnexpandedStorageURL ) +{ + OUString aExpandedSourceURL = expand_url( aSourceURL ); + if( aExpandedSourceURL != aSourceURL ) + { + aUnexpandedStorageURL = aSourceURL; + } + INetURLObject aInetObj( aExpandedSourceURL ); + OUString aExtension = aInetObj.getExtension(); + if( aExtension == "xlb" ) + { + // URL to xlb file + aLibInfoFileURL = aExpandedSourceURL; + aInetObj.removeSegment(); + aStorageURL = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + else + { + // URL to library folder + aStorageURL = aExpandedSourceURL; + aInetObj.insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aInetObj.setExtension( u"xlb" ); + aLibInfoFileURL = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } +} + +SfxLibrary* SfxLibraryContainer::getImplLib( const OUString& rLibraryName ) +{ + Any aLibAny = maNameContainer->getByName( rLibraryName ) ; + Reference< XNameAccess > xNameAccess; + aLibAny >>= xNameAccess; + SfxLibrary* pImplLib = static_cast< SfxLibrary* >( xNameAccess.get() ); + return pImplLib; +} + + +// Storing with password encryption + +// Empty implementation, avoids unnecessary implementation in dlgcont.cxx +bool SfxLibraryContainer::implStorePasswordLibrary( SfxLibrary*, + const OUString&, + const uno::Reference< embed::XStorage >&, + const uno::Reference< task::XInteractionHandler >& ) +{ + return false; +} + +bool SfxLibraryContainer::implStorePasswordLibrary( + SfxLibrary* /*pLib*/, + const OUString& /*aName*/, + const css::uno::Reference< css::embed::XStorage >& /*xStorage*/, + const OUString& /*aTargetURL*/, + const Reference< XSimpleFileAccess3 >& /*xToUseSFI*/, + const uno::Reference< task::XInteractionHandler >& ) +{ + return false; +} + +bool SfxLibraryContainer::implLoadPasswordLibrary( + SfxLibrary* /*pLib*/, + const OUString& /*Name*/, + bool /*bVerifyPasswordOnly*/ ) +{ + return true; +} + +OUString SfxLibraryContainer::createAppLibraryFolder( SfxLibrary* pLib, std::u16string_view aName ) +{ + OUString aLibDirPath = pLib->maStorageURL; + if( aLibDirPath.isEmpty() ) + { + INetURLObject aInetObj( o3tl::getToken(maLibraryPath, 1, ';') ); + aInetObj.insertName( aName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + checkStorageURL( aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), pLib->maLibInfoFileURL, + pLib->maStorageURL, pLib->maUnexpandedStorageURL ); + aLibDirPath = pLib->maStorageURL; + } + + if( !mxSFI->isFolder( aLibDirPath ) ) + { + try + { + mxSFI->createFolder( aLibDirPath ); + } + catch(const Exception& ) + {} + } + + return aLibDirPath; +} + +// Storing +void SfxLibraryContainer::implStoreLibrary( SfxLibrary* pLib, + std::u16string_view aName, + const uno::Reference< embed::XStorage >& xStorage ) +{ + Reference< XSimpleFileAccess3 > xDummySFA; + Reference< XInteractionHandler > xDummyHandler; + implStoreLibrary( pLib, aName, xStorage, u"", xDummySFA, xDummyHandler ); +} + +// New variant for library export +void SfxLibraryContainer::implStoreLibrary( SfxLibrary* pLib, + std::u16string_view aName, + const uno::Reference< embed::XStorage >& xStorage, + std::u16string_view aTargetURL, + const Reference< XSimpleFileAccess3 >& rToUseSFI, + const Reference< XInteractionHandler >& xHandler ) +{ + bool bLink = pLib->mbLink; + bool bStorage = xStorage.is() && !bLink; + + Sequence< OUString > aElementNames = pLib->getElementNames(); + sal_Int32 nNameCount = aElementNames.getLength(); + const OUString* pNames = aElementNames.getConstArray(); + + if( bStorage ) + { + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aElementName = pNames[ i ]; + OUString aStreamName = aElementName + ".xml"; + + if( !isLibraryElementValid( pLib->getByName( aElementName ) ) ) + { + SAL_WARN( + "basic", + "invalid library element \"" << aElementName << '"'); + continue; + } + try + { + uno::Reference< io::XStream > xElementStream = xStorage->openStreamElement( + aStreamName, + embed::ElementModes::READWRITE ); + // throw uno::RuntimeException(); // TODO: method must either return the stream or throw an exception + + uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY ); + SAL_WARN_IF( + !xProps.is(), "basic", + "The StorageStream must implement XPropertySet interface!"); + //if ( !xProps.is() ) //TODO + + if ( xProps.is() ) + { + xProps->setPropertyValue("MediaType", uno::Any( OUString( "text/xml" ) ) ); + + // #87671 Allow encryption + xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any( true ) ); + + Reference< XOutputStream > xOutput = xElementStream->getOutputStream(); + Reference< XNameContainer > xLib( pLib ); + writeLibraryElement( xLib, aElementName, xOutput ); + } + } + catch(const uno::Exception& ) + { + SAL_WARN("basic", "Problem during storing of library!"); + // TODO: error handling? + } + } + pLib->storeResourcesToStorage( xStorage ); + } + else + { + // Export? + bool bExport = !aTargetURL.empty(); + try + { + Reference< XSimpleFileAccess3 > xSFI = mxSFI; + if( rToUseSFI.is() ) + { + xSFI = rToUseSFI; + } + OUString aLibDirPath; + if( bExport ) + { + INetURLObject aInetObj( aTargetURL ); + aInetObj.insertName( aName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aLibDirPath = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( !xSFI->isFolder( aLibDirPath ) ) + { + xSFI->createFolder( aLibDirPath ); + } + pLib->storeResourcesToURL( aLibDirPath, xHandler ); + } + else + { + aLibDirPath = createAppLibraryFolder( pLib, aName ); + pLib->storeResources(); + } + + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aElementName = pNames[ i ]; + + INetURLObject aElementInetObj( aLibDirPath ); + aElementInetObj.insertName( aElementName, false, + INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aElementInetObj.setExtension( maLibElementFileExtension ); + OUString aElementPath( aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + if( !isLibraryElementValid( pLib->getByName( aElementName ) ) ) + { + SAL_WARN( + "basic", + "invalid library element \"" << aElementName << '"'); + continue; + } + + // TODO: Check modified + try + { + if( xSFI->exists( aElementPath ) ) + { + xSFI->kill( aElementPath ); + } + Reference< XOutputStream > xOutput = xSFI->openFileWrite( aElementPath ); + Reference< XNameContainer > xLib( pLib ); + writeLibraryElement( xLib, aElementName, xOutput ); + xOutput->closeOutput(); + } + catch(const Exception& ) + { + if( bExport ) + { + throw; + } + SfxErrorContext aEc( ERRCTX_SFX_SAVEDOC, aElementPath ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + } + } + catch(const Exception& ) + { + if( bExport ) + { + throw; + } + } + } +} + +void SfxLibraryContainer::implStoreLibraryIndexFile( SfxLibrary* pLib, + const ::xmlscript::LibDescriptor& rLib, + const uno::Reference< embed::XStorage >& xStorage ) +{ + Reference< XSimpleFileAccess3 > xDummySFA; + implStoreLibraryIndexFile( pLib, rLib, xStorage, u"", xDummySFA ); +} + +// New variant for library export +void SfxLibraryContainer::implStoreLibraryIndexFile( SfxLibrary* pLib, + const ::xmlscript::LibDescriptor& rLib, + const uno::Reference< embed::XStorage >& xStorage, + std::u16string_view aTargetURL, + const Reference< XSimpleFileAccess3 >& rToUseSFI ) +{ + // Create sax writer + Reference< XWriter > xWriter = xml::sax::Writer::create(mxContext); + + bool bLink = pLib->mbLink; + bool bStorage = xStorage.is() && !bLink; + + // Write info file + uno::Reference< io::XOutputStream > xOut; + uno::Reference< io::XStream > xInfoStream; + if( bStorage ) + { + OUString aStreamName = maInfoFileName + "-lb.xml"; + + try + { + xInfoStream = xStorage->openStreamElement( aStreamName, embed::ElementModes::READWRITE ); + SAL_WARN_IF(!xInfoStream.is(), "basic", "No stream!"); + uno::Reference< beans::XPropertySet > xProps( xInfoStream, uno::UNO_QUERY ); + // throw uno::RuntimeException(); // TODO + + if ( xProps.is() ) + { + xProps->setPropertyValue("MediaType", uno::Any( OUString("text/xml") ) ); + + // #87671 Allow encryption + xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any( true ) ); + + xOut = xInfoStream->getOutputStream(); + } + } + catch(const uno::Exception& ) + { + SAL_WARN("basic", "Problem during storing of library index file!"); + // TODO: error handling? + } + } + else + { + // Export? + bool bExport = !aTargetURL.empty(); + Reference< XSimpleFileAccess3 > xSFI = mxSFI; + if( rToUseSFI.is() ) + { + xSFI = rToUseSFI; + } + OUString aLibInfoPath; + if( bExport ) + { + INetURLObject aInetObj( aTargetURL ); + aInetObj.insertName( rLib.aName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + OUString aLibDirPath = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( !xSFI->isFolder( aLibDirPath ) ) + { + xSFI->createFolder( aLibDirPath ); + } + aInetObj.insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aInetObj.setExtension( u"xlb" ); + aLibInfoPath = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + else + { + createAppLibraryFolder( pLib, rLib.aName ); + aLibInfoPath = pLib->maLibInfoFileURL; + } + + try + { + if( xSFI->exists( aLibInfoPath ) ) + { + xSFI->kill( aLibInfoPath ); + } + xOut = xSFI->openFileWrite( aLibInfoPath ); + } + catch(const Exception& ) + { + if( bExport ) + { + throw; + } + SfxErrorContext aEc( ERRCTX_SFX_SAVEDOC, aLibInfoPath ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + } + if( !xOut.is() ) + { + SAL_WARN("basic", "couldn't open output stream"); + return; + } + xWriter->setOutputStream( xOut ); + xmlscript::exportLibrary( xWriter, rLib ); +} + + +bool SfxLibraryContainer::implLoadLibraryIndexFile( SfxLibrary* pLib, + ::xmlscript::LibDescriptor& rLib, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& aIndexFileName ) +{ + Reference< XParser > xParser = xml::sax::Parser::create(mxContext); + + bool bStorage = false; + if( pLib ) + { + bool bLink = pLib->mbLink; + bStorage = xStorage.is() && !bLink; + } + + // Read info file + uno::Reference< io::XInputStream > xInput; + OUString aLibInfoPath; + if( bStorage ) + { + aLibInfoPath = maInfoFileName + "-lb.xml"; + + try + { + uno::Reference< io::XStream > xInfoStream = + xStorage->openStreamElement( aLibInfoPath, embed::ElementModes::READ ); + xInput = xInfoStream->getInputStream(); + } + catch(const uno::Exception& ) + {} + } + else + { + // Create Input stream + //String aLibInfoPath; // attention: THIS PROBLEM MUST BE REVIEWED BY SCRIPTING OWNER!!! + + if( pLib ) + { + createAppLibraryFolder( pLib, rLib.aName ); + aLibInfoPath = pLib->maLibInfoFileURL; + } + else + { + aLibInfoPath = aIndexFileName; + } + try + { + xInput = mxSFI->openFileRead( aLibInfoPath ); + } + catch(const Exception& ) + { + xInput.clear(); + if( !GbMigrationSuppressErrors ) + { + SfxErrorContext aEc( ERRCTX_SFX_LOADBASIC, aLibInfoPath ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + } + } + if( !xInput.is() ) + { + return false; + } + + InputSource source; + source.aInputStream = xInput; + source.sSystemId = aLibInfoPath; + + // start parsing + try + { + xParser->setDocumentHandler( ::xmlscript::importLibrary( rLib ) ); + xParser->parseStream( source ); + } + catch(const Exception& ) + { + SAL_WARN("basic", "Parsing error"); + SfxErrorContext aEc( ERRCTX_SFX_LOADBASIC, aLibInfoPath ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + return false; + } + + if( !pLib ) + { + Reference< XNameContainer > xLib = createLibrary( rLib.aName ); + pLib = static_cast< SfxLibrary* >( xLib.get() ); + pLib->mbLoaded = false; + rLib.aStorageURL = aIndexFileName; + checkStorageURL( rLib.aStorageURL, pLib->maLibInfoFileURL, pLib->maStorageURL, + pLib->maUnexpandedStorageURL ); + + implImportLibDescriptor( pLib, rLib ); + } + + return true; +} + +void SfxLibraryContainer::implImportLibDescriptor( SfxLibrary* pLib, + ::xmlscript::LibDescriptor const & rLib ) +{ + if( pLib->mbInitialised ) + return; + + sal_Int32 nElementCount = rLib.aElementNames.getLength(); + const OUString* pElementNames = rLib.aElementNames.getConstArray(); + Any aDummyElement = createEmptyLibraryElement(); + for( sal_Int32 i = 0 ; i < nElementCount ; i++ ) + { + pLib->maNameContainer->insertByName( pElementNames[i], aDummyElement ); + } + pLib->mbPasswordProtected = rLib.bPasswordProtected; + pLib->mbReadOnly = rLib.bReadOnly; + pLib->mbPreload = rLib.bPreload; + pLib->implSetModified( false ); + pLib->mbInitialised = true; +} + + +// Methods of new XLibraryStorage interface? +void SfxLibraryContainer::storeLibraries_Impl( const uno::Reference< embed::XStorage >& i_rStorage, + bool bComplete ) +{ + const Sequence< OUString > aNames = maNameContainer->getElementNames(); + const sal_Int32 nNameCount = aNames.getLength(); + const OUString* pName = aNames.getConstArray(); + const OUString* pNamesEnd = aNames.getConstArray() + nNameCount; + + // Don't count libs from shared index file + sal_Int32 nLibsToSave = nNameCount; + for( ; pName != pNamesEnd; ++pName ) + { + SfxLibrary* pImplLib = getImplLib( *pName ); + if( pImplLib->mbSharedIndexFile || pImplLib->mbExtension ) + { + nLibsToSave--; + } + } + // Write to storage? + bool bStorage = i_rStorage.is(); + uno::Reference< embed::XStorage > xSourceLibrariesStor; + uno::Reference< embed::XStorage > xTargetLibrariesStor; + OUString sTempTargetStorName; + const bool bInplaceStorage = bStorage && ( i_rStorage == mxStorage ); + + if( nLibsToSave == 0 ) + { + if ( bInplaceStorage && mxStorage->hasByName(maLibrariesDir) ) + { + mxStorage->removeElement(maLibrariesDir); + } + return; + } + + if ( bStorage ) + { + // Don't write if only empty standard lib exists + if ( ( nLibsToSave == 1 ) && ( aNames[0] == "Standard" ) ) + { + Any aLibAny = maNameContainer->getByName( aNames[0] ); + Reference< XNameAccess > xNameAccess; + aLibAny >>= xNameAccess; + if ( ! xNameAccess->hasElements() ) + { + if ( bInplaceStorage && mxStorage->hasByName(maLibrariesDir) ) + { + mxStorage->removeElement(maLibrariesDir); + } + return; + } + } + + // create the empty target storage + try + { + OUString sTargetLibrariesStoreName; + if ( bInplaceStorage ) + { + // create a temporary target storage + const OUStringBuffer aTempTargetNameBase = maLibrariesDir + "_temp_"; + sal_Int32 index = 0; + do + { + OUStringBuffer aTempTargetName( aTempTargetNameBase ); + aTempTargetName.append( index++ ); + + sTargetLibrariesStoreName = aTempTargetName.makeStringAndClear(); + if ( !i_rStorage->hasByName( sTargetLibrariesStoreName ) ) + { + break; + } + } + while ( true ); + sTempTargetStorName = sTargetLibrariesStoreName; + } + else + { + sTargetLibrariesStoreName = maLibrariesDir; + if ( i_rStorage->hasByName( sTargetLibrariesStoreName ) ) + { + i_rStorage->removeElement( sTargetLibrariesStoreName ); + } + } + + xTargetLibrariesStor.set( i_rStorage->openStorageElement( sTargetLibrariesStoreName, embed::ElementModes::READWRITE ), UNO_SET_THROW ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + return; + } + + // open the source storage which might be used to copy yet-unmodified libraries + try + { + if ( mxStorage->hasByName( maLibrariesDir ) || bInplaceStorage ) + { + xSourceLibrariesStor = mxStorage->openStorageElement( maLibrariesDir, + bInplaceStorage ? embed::ElementModes::READWRITE : embed::ElementModes::READ ); + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + return; + } + } + + int iArray = 0; + pName = aNames.getConstArray(); + ::xmlscript::LibDescriptor aLibDescriptorForExtensionLibs; + auto pLibArray = std::make_unique< ::xmlscript::LibDescriptorArray > ( nLibsToSave ); + for( ; pName != pNamesEnd; ++pName ) + { + SfxLibrary* pImplLib = getImplLib( *pName ); + if( pImplLib->mbSharedIndexFile ) + { + continue; + } + const bool bExtensionLib = pImplLib->mbExtension; + ::xmlscript::LibDescriptor& rLib = bExtensionLib ? + aLibDescriptorForExtensionLibs : pLibArray->mpLibs[iArray]; + if( !bExtensionLib ) + { + iArray++; + } + rLib.aName = *pName; + + rLib.bLink = pImplLib->mbLink; + if( !bStorage || pImplLib->mbLink ) + { + rLib.aStorageURL = ( pImplLib->maUnexpandedStorageURL.getLength() ) ? + pImplLib->maUnexpandedStorageURL : pImplLib->maLibInfoFileURL; + } + rLib.bReadOnly = pImplLib->mbReadOnly; + rLib.bPreload = pImplLib->mbPreload; + rLib.bPasswordProtected = pImplLib->mbPasswordProtected; + rLib.aElementNames = pImplLib->getElementNames(); + + if( pImplLib->implIsModified() || bComplete ) + { +// Testing pImplLib->implIsModified() is not reliable, +// IMHO the value of pImplLib->implIsModified() should +// reflect whether the library ( in-memory ) model +// is in sync with the library container's own storage. Currently +// whenever the library model is written to *any* storage +// pImplLib->implSetModified( sal_False ) is called +// The way the code works, especially the way that sfx uses +// temp storage when saving ( and later sets the root storage of the +// library container ) and similar madness in dbaccess means some surgery +// is required to make it possible to successfully use this optimisation +// It would be possible to do the implSetModified() call below only +// conditionally, but that would require an additional boolean to be +// passed in via the XStorageBasedDocument::storeLibrariesToStorage()... +// fdo#68983: If there's a password and the password is not known, only +// copying the storage works! + // Can we simply copy the storage? + bool isCopyStorage = !mbOldInfoFormat && !mbOasis2OOoFormat + && !pImplLib->isLoadedStorable() + && xSourceLibrariesStor.is() /* null for user profile */; + if (isCopyStorage) + { + try + { + (void)xSourceLibrariesStor->isStorageElement(rLib.aName); + } + catch (container::NoSuchElementException const&) + { + isCopyStorage = false; + } + } + if (isCopyStorage) + { + try + { + xSourceLibrariesStor->copyElementTo( rLib.aName, xTargetLibrariesStor, rLib.aName ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + // TODO: error handling? + } + } + else + { + uno::Reference< embed::XStorage > xLibraryStor; + if( bStorage ) + { +#if OSL_DEBUG_LEVEL > 0 + try + { +#endif + xLibraryStor = xTargetLibrariesStor->openStorageElement( + rLib.aName, + embed::ElementModes::READWRITE ); +#if OSL_DEBUG_LEVEL > 0 + } + catch(const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( + "basic", + "couldn't create sub storage for library \"" << rLib.aName << "\""); + throw; + } +#endif + } + + // Maybe lib is not loaded?! + if( bComplete ) + { + loadLibrary( rLib.aName ); + } + if( pImplLib->mbPasswordProtected ) + { + implStorePasswordLibrary( pImplLib, rLib.aName, xLibraryStor, uno::Reference< task::XInteractionHandler >() ); + // TODO: Check return value + } + else + { + implStoreLibrary( pImplLib, rLib.aName, xLibraryStor ); + } + implStoreLibraryIndexFile( pImplLib, rLib, xLibraryStor ); + if( bStorage ) + { + try + { + uno::Reference< embed::XTransactedObject > xTransact( xLibraryStor, uno::UNO_QUERY_THROW ); + xTransact->commit(); + } + catch(const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + // TODO: error handling + throw; + } + } + } + maModifiable.setModified( true ); + pImplLib->implSetModified( false ); + } + + // For container info ReadOnly refers to mbReadOnlyLink + rLib.bReadOnly = pImplLib->mbReadOnlyLink; + } + + // if we did an in-place save into a storage (i.e. a save into the storage we were already based on), + // then we need to clean up the temporary storage we used for this + if ( bInplaceStorage && !sTempTargetStorName.isEmpty() ) + { + SAL_WARN_IF( + !xSourceLibrariesStor.is(), "basic", + ("SfxLibrariesContainer::storeLibraries_impl: unexpected: we should" + " have a source storage here!")); + try + { + // for this, we first remove everything from the source storage, then copy the complete content + // from the temporary target storage. From then on, what used to be the "source storage" becomes + // the "target storage" for all subsequent operations. + + // (We cannot simply remove the storage, denoted by maLibrariesDir, from i_rStorage - there might be + // open references to it.) + + if ( xSourceLibrariesStor.is() ) + { + // remove + const Sequence< OUString > aRemoveNames( xSourceLibrariesStor->getElementNames() ); + for ( auto const & removeName : aRemoveNames ) + { + xSourceLibrariesStor->removeElement( removeName ); + } + + // copy + const Sequence< OUString > aCopyNames( xTargetLibrariesStor->getElementNames() ); + for ( auto const & copyName : aCopyNames ) + { + xTargetLibrariesStor->copyElementTo( copyName, xSourceLibrariesStor, copyName ); + } + } + + // close and remove temp target + xTargetLibrariesStor->dispose(); + i_rStorage->removeElement( sTempTargetStorName ); + xTargetLibrariesStor.clear(); + sTempTargetStorName.clear(); + + // adjust target + xTargetLibrariesStor = xSourceLibrariesStor; + xSourceLibrariesStor.clear(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + throw; + } + } + + if( !mbOldInfoFormat && !maModifiable.isModified() ) + { + return; + } + maModifiable.setModified( false ); + mbOldInfoFormat = false; + + // Write library container info + // Create sax writer + Reference< XWriter > xWriter = xml::sax::Writer::create(mxContext); + + // Write info file + uno::Reference< io::XOutputStream > xOut; + uno::Reference< io::XStream > xInfoStream; + if( bStorage ) + { + OUString aStreamName = maInfoFileName + "-lc.xml"; + + try + { + xInfoStream = xTargetLibrariesStor->openStreamElement( aStreamName, embed::ElementModes::READWRITE ); + uno::Reference< beans::XPropertySet > xProps( xInfoStream, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue("MediaType", uno::Any( OUString( "text/xml" ) ) ); + + // #87671 Allow encryption + xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any( true ) ); + + xOut = xInfoStream->getOutputStream(); + } + catch(const uno::Exception& ) + { + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + } + else + { + // Create Output stream + INetURLObject aLibInfoInetObj( o3tl::getToken(maLibraryPath, 1, ';') ); + aLibInfoInetObj.insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aLibInfoInetObj.setExtension( u"xlc" ); + OUString aLibInfoPath( aLibInfoInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + try + { + if( mxSFI->exists( aLibInfoPath ) ) + { + mxSFI->kill( aLibInfoPath ); + } + xOut = mxSFI->openFileWrite( aLibInfoPath ); + } + catch(const Exception& ) + { + xOut.clear(); + SfxErrorContext aEc( ERRCTX_SFX_SAVEDOC, aLibInfoPath ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + + } + if( !xOut.is() ) + { + SAL_WARN("basic", "couldn't open output stream"); + return; + } + + xWriter->setOutputStream( xOut ); + + try + { + xmlscript::exportLibraryContainer( xWriter, pLibArray.get() ); + if ( bStorage ) + { + uno::Reference< embed::XTransactedObject > xTransact( xTargetLibrariesStor, uno::UNO_QUERY_THROW ); + xTransact->commit(); + } + } + catch(const uno::Exception& ) + { + SAL_WARN("basic", "Problem during storing of libraries!"); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } +} + + +// Methods XElementAccess +Type SAL_CALL SfxLibraryContainer::getElementType() +{ + LibraryContainerMethodGuard aGuard( *this ); + return maNameContainer->getElementType(); +} + +sal_Bool SfxLibraryContainer::hasElements() +{ + LibraryContainerMethodGuard aGuard( *this ); + bool bRet = maNameContainer->hasElements(); + return bRet; +} + +// Methods XNameAccess +Any SfxLibraryContainer::getByName( const OUString& aName ) +{ + LibraryContainerMethodGuard aGuard( *this ); + Any aRetAny = maNameContainer->getByName( aName ) ; + return aRetAny; +} + +Sequence< OUString > SfxLibraryContainer::getElementNames() +{ + LibraryContainerMethodGuard aGuard( *this ); + return maNameContainer->getElementNames(); +} + +sal_Bool SfxLibraryContainer::hasByName( const OUString& aName ) +{ + LibraryContainerMethodGuard aGuard( *this ); + return maNameContainer->hasByName( aName ) ; +} + +// Methods XLibraryContainer +Reference< XNameContainer > SAL_CALL SfxLibraryContainer::createLibrary( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + rtl::Reference<SfxLibrary> pNewLib = implCreateLibrary( Name ); + pNewLib->maLibElementFileExtension = maLibElementFileExtension; + + createVariableURL( pNewLib->maUnexpandedStorageURL, Name, maInfoFileName, true ); + + Reference< XNameAccess > xNameAccess( pNewLib ); + Any aElement; + aElement <<= xNameAccess; + maNameContainer->insertByName( Name, aElement ); + maModifiable.setModified( true ); + Reference< XNameContainer > xRet( xNameAccess, UNO_QUERY ); + return xRet; +} + +Reference< XNameAccess > SAL_CALL SfxLibraryContainer::createLibraryLink + ( const OUString& Name, const OUString& StorageURL, sal_Bool ReadOnly ) +{ + LibraryContainerMethodGuard aGuard( *this ); + // TODO: Check other reasons to force ReadOnly status + //if( !ReadOnly ) + //{ + //} + + OUString aLibInfoFileURL; + OUString aLibDirURL; + OUString aUnexpandedStorageURL; + checkStorageURL( StorageURL, aLibInfoFileURL, aLibDirURL, aUnexpandedStorageURL ); + + + rtl::Reference<SfxLibrary> pNewLib = implCreateLibraryLink( Name, aLibInfoFileURL, aLibDirURL, ReadOnly ); + pNewLib->maLibElementFileExtension = maLibElementFileExtension; + pNewLib->maUnexpandedStorageURL = aUnexpandedStorageURL; + pNewLib->maOriginalStorageURL = StorageURL; + + uno::Reference< embed::XStorage > xDummyStor; + ::xmlscript::LibDescriptor aLibDesc; + implLoadLibraryIndexFile( pNewLib.get(), aLibDesc, xDummyStor, OUString() ); + implImportLibDescriptor( pNewLib.get(), aLibDesc ); + + Reference< XNameAccess > xRet( pNewLib ); + Any aElement; + aElement <<= xRet; + maNameContainer->insertByName( Name, aElement ); + maModifiable.setModified( true ); + + if( StorageURL.indexOf( "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE" ) != -1 ) + { + pNewLib->mbExtension = true; + } + else if( StorageURL.indexOf( "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE" ) != -1 + || StorageURL.indexOf( "vnd.sun.star.expand:$BUNDLED_EXTENSIONS" ) != -1 ) + { + pNewLib->mbExtension = true; + pNewLib->mbReadOnly = true; + } + + return xRet; +} + +void SAL_CALL SfxLibraryContainer::removeLibrary( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + // Get and hold library before removing + Any aLibAny = maNameContainer->getByName( Name ) ; + Reference< XNameAccess > xNameAccess; + aLibAny >>= xNameAccess; + SfxLibrary* pImplLib = static_cast< SfxLibrary* >( xNameAccess.get() ); + if( pImplLib->mbReadOnly && !pImplLib->mbLink ) + { + throw IllegalArgumentException("readonly && !link", static_cast<cppu::OWeakObject*>(this), 1); + } + // Remove from container + maNameContainer->removeByName( Name ); + maModifiable.setModified( true ); + + // Delete library files, but not for linked libraries + if( pImplLib->mbLink ) + return; + + if( mxStorage.is() ) + { + return; + } + if( xNameAccess->hasElements() ) + { + Sequence< OUString > aNames = pImplLib->getElementNames(); + sal_Int32 nNameCount = aNames.getLength(); + const OUString* pNames = aNames.getConstArray(); + for( sal_Int32 i = 0 ; i < nNameCount ; ++i, ++pNames ) + { + pImplLib->removeElementWithoutChecks( *pNames, SfxLibrary::LibraryContainerAccess() ); + } + } + + // Delete index file + createAppLibraryFolder( pImplLib, Name ); + OUString aLibInfoPath = pImplLib->maLibInfoFileURL; + try + { + if( mxSFI->exists( aLibInfoPath ) ) + { + mxSFI->kill( aLibInfoPath ); + } + } + catch(const Exception& ) {} + + // Delete folder if empty + INetURLObject aInetObj( o3tl::getToken(maLibraryPath, 1, ';') ); + aInetObj.insertName( Name, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + OUString aLibDirPath = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + try + { + if( mxSFI->isFolder( aLibDirPath ) ) + { + Sequence< OUString > aContentSeq = mxSFI->getFolderContents( aLibDirPath, true ); + sal_Int32 nCount = aContentSeq.getLength(); + if( !nCount ) + { + mxSFI->kill( aLibDirPath ); + } + } + } + catch(const Exception& ) + { + } +} + +sal_Bool SAL_CALL SfxLibraryContainer::isLibraryLoaded( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + bool bRet = pImplLib->mbLoaded; + return bRet; +} + + +void SAL_CALL SfxLibraryContainer::loadLibrary( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + Any aLibAny = maNameContainer->getByName( Name ) ; + Reference< XNameAccess > xNameAccess; + aLibAny >>= xNameAccess; + SfxLibrary* pImplLib = static_cast< SfxLibrary* >( xNameAccess.get() ); + + bool bLoaded = pImplLib->mbLoaded; + pImplLib->mbLoaded = true; + if( bLoaded || !xNameAccess->hasElements() ) + return; + + if( pImplLib->mbPasswordProtected ) + { + implLoadPasswordLibrary( pImplLib, Name ); + return; + } + + bool bLink = pImplLib->mbLink; + bool bStorage = mxStorage.is() && !bLink; + + uno::Reference< embed::XStorage > xLibrariesStor; + uno::Reference< embed::XStorage > xLibraryStor; + if( bStorage ) + { +#if OSL_DEBUG_LEVEL > 0 + try + { +#endif + xLibrariesStor = mxStorage->openStorageElement( maLibrariesDir, embed::ElementModes::READ ); + SAL_WARN_IF( + !xLibrariesStor.is(), "basic", + ("The method must either throw exception or return a" + " storage!")); + if ( !xLibrariesStor.is() ) + { + throw uno::RuntimeException("null returned from openStorageElement",static_cast< cppu::OWeakObject * >(this)); + } + + xLibraryStor = xLibrariesStor->openStorageElement( Name, embed::ElementModes::READ ); + SAL_WARN_IF( + !xLibraryStor.is(), "basic", + ("The method must either throw exception or return a" + " storage!")); + if ( !xLibrariesStor.is() ) + { + throw uno::RuntimeException("null returned from openStorageElement",static_cast< cppu::OWeakObject * >(this)); + } +#if OSL_DEBUG_LEVEL > 0 + } + catch(const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( + "basic", + "couldn't open sub storage for library \"" << Name << "\""); + throw; + } +#endif + } + + Sequence< OUString > aNames = pImplLib->getElementNames(); + sal_Int32 nNameCount = aNames.getLength(); + const OUString* pNames = aNames.getConstArray(); + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aElementName = pNames[ i ]; + + OUString aFile; + uno::Reference< io::XInputStream > xInStream; + + if( bStorage ) + { + uno::Reference< io::XStream > xElementStream; + + aFile = aElementName + ".xml"; + + try + { + xElementStream = xLibraryStor->openStreamElement( aFile, embed::ElementModes::READ ); + } + catch(const uno::Exception& ) + {} + + if( !xElementStream.is() ) + { + // Check for EA2 document version with wrong extensions + aFile = aElementName + "." + maLibElementFileExtension; + try + { + xElementStream = xLibraryStor->openStreamElement( aFile, embed::ElementModes::READ ); + } + catch(const uno::Exception& ) + {} + } + + if ( xElementStream.is() ) + { + xInStream = xElementStream->getInputStream(); + } + if ( !xInStream.is() ) + { + SAL_WARN( + "basic", + "couldn't open library element stream - attempted to" + " open library \"" << Name << '"'); + throw RuntimeException("couldn't open library element stream", *this); + } + } + else + { + OUString aLibDirPath = pImplLib->maStorageURL; + INetURLObject aElementInetObj( aLibDirPath ); + aElementInetObj.insertName( aElementName, false, + INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aElementInetObj.setExtension( maLibElementFileExtension ); + aFile = aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + Reference< XNameContainer > xLib( pImplLib ); + Any aAny = importLibraryElement( xLib, aElementName, + aFile, xInStream ); + if( pImplLib->hasByName( aElementName ) ) + { + if( aAny.hasValue() ) + { + pImplLib->maNameContainer->replaceByName( aElementName, aAny ); + } + } + else + { + pImplLib->maNameContainer->insertNoCheck(aElementName, aAny); + } + } + pImplLib->implSetModified( false ); +} + +// Methods XLibraryContainer2 +sal_Bool SAL_CALL SfxLibraryContainer::isLibraryLink( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + bool bRet = pImplLib->mbLink; + return bRet; +} + +OUString SAL_CALL SfxLibraryContainer::getLibraryLinkURL( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + bool bLink = pImplLib->mbLink; + if( !bLink ) + { + throw IllegalArgumentException("!link", static_cast<cppu::OWeakObject*>(this), 1); + } + OUString aRetStr = pImplLib->maLibInfoFileURL; + return aRetStr; +} + +sal_Bool SAL_CALL SfxLibraryContainer::isLibraryReadOnly( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + bool bRet = pImplLib->mbReadOnly || (pImplLib->mbLink && pImplLib->mbReadOnlyLink); + return bRet; +} + +void SAL_CALL SfxLibraryContainer::setLibraryReadOnly( const OUString& Name, sal_Bool bReadOnly ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + if( pImplLib->mbLink ) + { + if( pImplLib->mbReadOnlyLink != bool(bReadOnly) ) + { + pImplLib->mbReadOnlyLink = bReadOnly; + pImplLib->implSetModified( true ); + maModifiable.setModified( true ); + } + } + else + { + if( pImplLib->mbReadOnly != bool(bReadOnly) ) + { + pImplLib->mbReadOnly = bReadOnly; + pImplLib->implSetModified( true ); + } + } +} + +void SAL_CALL SfxLibraryContainer::renameLibrary( const OUString& Name, const OUString& NewName ) +{ + LibraryContainerMethodGuard aGuard( *this ); + if( maNameContainer->hasByName( NewName ) ) + { + throw ElementExistException(); + } + // Get and hold library before removing + Any aLibAny = maNameContainer->getByName( Name ) ; + + // #i24094 Maybe lib is not loaded! + Reference< XNameAccess > xNameAccess; + aLibAny >>= xNameAccess; + SfxLibrary* pImplLib = static_cast< SfxLibrary* >( xNameAccess.get() ); + if( pImplLib->mbPasswordProtected && !pImplLib->mbPasswordVerified ) + { + return; // Lib with unverified password cannot be renamed + } + loadLibrary( Name ); + + // Remove from container + maNameContainer->removeByName( Name ); + maModifiable.setModified( true ); + + // Rename library folder, but not for linked libraries + bool bMovedSuccessful = true; + + // Rename files + bool bStorage = mxStorage.is(); + if( !bStorage && !pImplLib->mbLink ) + { + bMovedSuccessful = false; + + OUString aLibDirPath = pImplLib->maStorageURL; + + INetURLObject aDestInetObj( o3tl::getToken(maLibraryPath, 1, ';')); + aDestInetObj.insertName( NewName, true, INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + OUString aDestDirPath = aDestInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + // Store new URL + OUString aLibInfoFileURL = pImplLib->maLibInfoFileURL; + checkStorageURL( aDestDirPath, pImplLib->maLibInfoFileURL, pImplLib->maStorageURL, + pImplLib->maUnexpandedStorageURL ); + + try + { + if( mxSFI->isFolder( aLibDirPath ) ) + { + if( !mxSFI->isFolder( aDestDirPath ) ) + { + mxSFI->createFolder( aDestDirPath ); + } + // Move index file + try + { + if( mxSFI->exists( pImplLib->maLibInfoFileURL ) ) + { + mxSFI->kill( pImplLib->maLibInfoFileURL ); + } + mxSFI->move( aLibInfoFileURL, pImplLib->maLibInfoFileURL ); + } + catch(const Exception& ) + { + } + + Sequence< OUString > aElementNames = xNameAccess->getElementNames(); + sal_Int32 nNameCount = aElementNames.getLength(); + const OUString* pNames = aElementNames.getConstArray(); + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aElementName = pNames[ i ]; + + INetURLObject aElementInetObj( aLibDirPath ); + aElementInetObj.insertName( aElementName, false, + INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aElementInetObj.setExtension( maLibElementFileExtension ); + OUString aElementPath( aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + INetURLObject aElementDestInetObj( aDestDirPath ); + aElementDestInetObj.insertName( aElementName, false, + INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aElementDestInetObj.setExtension( maLibElementFileExtension ); + OUString aDestElementPath( aElementDestInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + try + { + if( mxSFI->exists( aDestElementPath ) ) + { + mxSFI->kill( aDestElementPath ); + } + mxSFI->move( aElementPath, aDestElementPath ); + } + catch(const Exception& ) + { + } + } + pImplLib->storeResourcesAsURL( aDestDirPath, NewName ); + + // Delete folder if empty + Sequence< OUString > aContentSeq = mxSFI->getFolderContents( aLibDirPath, true ); + sal_Int32 nCount = aContentSeq.getLength(); + if( !nCount ) + { + mxSFI->kill( aLibDirPath ); + } + + bMovedSuccessful = true; + pImplLib->implSetModified( true ); + } + } + catch(const Exception& ) + { + // Restore old library + maNameContainer->insertByName( Name, aLibAny ) ; + } + } + + if( bStorage && !pImplLib->mbLink ) + { + pImplLib->implSetModified( true ); + } + if( bMovedSuccessful ) + { + maNameContainer->insertByName( NewName, aLibAny ) ; + } +} + + +// Methods XInitialization +void SAL_CALL SfxLibraryContainer::initialize( const Sequence< Any >& _rArguments ) +{ + LibraryContainerMethodGuard aGuard( *this ); + sal_Int32 nArgCount = _rArguments.getLength(); + if ( nArgCount != 1 ) + throw IllegalArgumentException("too many args", static_cast<cppu::OWeakObject*>(this), -1); + + OUString sInitialDocumentURL; + Reference< XStorageBasedDocument > xDocument; + if ( _rArguments[0] >>= sInitialDocumentURL ) + { + init( sInitialDocumentURL, nullptr ); + return; + } + + if ( _rArguments[0] >>= xDocument ) + { + initializeFromDocument( xDocument ); + return; + } + throw IllegalArgumentException("arg1 unknown type", static_cast<cppu::OWeakObject*>(this), 1); + +} + +void SfxLibraryContainer::initializeFromDocument( const Reference< XStorageBasedDocument >& _rxDocument ) +{ + // check whether this is a valid OfficeDocument, and obtain the document's root storage + Reference< XStorage > xDocStorage; + try + { + Reference< XServiceInfo > xSI( _rxDocument, UNO_QUERY_THROW ); + if ( xSI->supportsService("com.sun.star.document.OfficeDocument")) + { + xDocStorage.set( _rxDocument->getDocumentStorage(), UNO_SET_THROW ); + } + Reference< XModel > xDocument( _rxDocument, UNO_QUERY_THROW ); + Reference< XComponent > xDocComponent( _rxDocument, UNO_QUERY_THROW ); + + mxOwnerDocument = xDocument; + startComponentListening( xDocComponent ); + } + catch( const Exception& ) { } + + if ( !xDocStorage.is() ) + { + throw IllegalArgumentException("no doc storage", static_cast<cppu::OWeakObject*>(this), 1); + } + init( OUString(), xDocStorage ); +} + +// OEventListenerAdapter +void SfxLibraryContainer::_disposing( const EventObject& _rSource ) +{ +#if OSL_DEBUG_LEVEL > 0 + Reference< XModel > xDocument( mxOwnerDocument.get(), UNO_QUERY ); + SAL_WARN_IF( + xDocument != _rSource.Source || !xDocument.is(), "basic", + "SfxLibraryContainer::_disposing: where does this come from?"); +#else + (void)_rSource; +#endif + dispose(); +} + +// OComponentHelper +void SAL_CALL SfxLibraryContainer::disposing() +{ + Reference< XModel > xModel = mxOwnerDocument; + EventObject aEvent( xModel ); + maVBAScriptListeners.disposeAndClear( aEvent ); + stopAllComponentListening(); + mxOwnerDocument.clear(); +} + +// Methods XLibraryContainerPassword +sal_Bool SAL_CALL SfxLibraryContainer::isLibraryPasswordProtected( const OUString& ) +{ + return false; +} + +sal_Bool SAL_CALL SfxLibraryContainer::isLibraryPasswordVerified( const OUString& ) +{ + throw IllegalArgumentException(); +} + +sal_Bool SAL_CALL SfxLibraryContainer::verifyLibraryPassword( const OUString&, const OUString& ) +{ + throw IllegalArgumentException(); +} + +void SAL_CALL SfxLibraryContainer::changeLibraryPassword(const OUString&, const OUString&, const OUString& ) +{ + throw IllegalArgumentException(); +} + +// Methods XContainer +void SAL_CALL SfxLibraryContainer::addContainerListener( const Reference< XContainerListener >& xListener ) +{ + LibraryContainerMethodGuard aGuard( *this ); + maNameContainer->setEventSource( static_cast< XInterface* >( static_cast<OWeakObject*>(this) ) ); + maNameContainer->addContainerListener( xListener ); +} + +void SAL_CALL SfxLibraryContainer::removeContainerListener( const Reference< XContainerListener >& xListener ) +{ + LibraryContainerMethodGuard aGuard( *this ); + maNameContainer->removeContainerListener( xListener ); +} + +// Methods XLibraryContainerExport +void SAL_CALL SfxLibraryContainer::exportLibrary( const OUString& Name, const OUString& URL, + const Reference< XInteractionHandler >& Handler ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + + Reference< XSimpleFileAccess3 > xToUseSFI; + if( Handler.is() ) + { + xToUseSFI = ucb::SimpleFileAccess::create( mxContext ); + xToUseSFI->setInteractionHandler( Handler ); + } + + // Maybe lib is not loaded?! + loadLibrary( Name ); + + uno::Reference< css::embed::XStorage > xDummyStor; + if( pImplLib->mbPasswordProtected ) + { + implStorePasswordLibrary( pImplLib, Name, xDummyStor, URL, xToUseSFI, Handler ); + } + else + { + implStoreLibrary( pImplLib, Name, xDummyStor, URL, xToUseSFI, Handler ); + } + ::xmlscript::LibDescriptor aLibDesc; + aLibDesc.aName = Name; + aLibDesc.bLink = false; // Link status gets lost? + aLibDesc.bReadOnly = pImplLib->mbReadOnly; + aLibDesc.bPreload = false; // Preload status gets lost? + aLibDesc.bPasswordProtected = pImplLib->mbPasswordProtected; + aLibDesc.aElementNames = pImplLib->getElementNames(); + + implStoreLibraryIndexFile( pImplLib, aLibDesc, xDummyStor, URL, xToUseSFI ); +} + +OUString SfxLibraryContainer::expand_url( const OUString& url ) +{ + if (url.startsWithIgnoreAsciiCase( "vnd.sun.star.expand:" )) + { + return comphelper::getExpandedUri(mxContext, url); + } + else if( mxStringSubstitution.is() ) + { + OUString ret( mxStringSubstitution->substituteVariables( url, false ) ); + return ret; + } + else + { + return url; + } +} + +//XLibraryContainer3 +OUString SAL_CALL SfxLibraryContainer::getOriginalLibraryLinkURL( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + bool bLink = pImplLib->mbLink; + if( !bLink ) + { + throw IllegalArgumentException("!link", static_cast<cppu::OWeakObject*>(this), 1); + } + OUString aRetStr = pImplLib->maOriginalStorageURL; + return aRetStr; +} + + +// XVBACompatibility +sal_Bool SAL_CALL SfxLibraryContainer::getVBACompatibilityMode() +{ + return mbVBACompat; +} + +void SAL_CALL SfxLibraryContainer::setVBACompatibilityMode( sal_Bool _vbacompatmodeon ) +{ + /* The member variable mbVBACompat must be set first, the following call + to getBasicManager() may call getVBACompatibilityMode() which returns + this value. */ + mbVBACompat = _vbacompatmodeon; + BasicManager* pBasMgr = getBasicManager(); + if( !pBasMgr ) + return; + + // get the standard library + OUString aLibName = pBasMgr->GetName(); + if ( aLibName.isEmpty()) + { + aLibName = "Standard"; + } + if( StarBASIC* pBasic = pBasMgr->GetLib( aLibName ) ) + { + pBasic->SetVBAEnabled( _vbacompatmodeon ); + } + /* If in VBA compatibility mode, force creation of the VBA Globals + object. Each application will create an instance of its own + implementation and store it in its Basic manager. Implementations + will do all necessary additional initialization, such as + registering the global "This***Doc" UNO constant, starting the + document events processor etc. + */ + if( mbVBACompat ) try + { + Reference< XModel > xModel( mxOwnerDocument ); // weak-ref -> ref + Reference< XMultiServiceFactory > xFactory( xModel, UNO_QUERY_THROW ); + xFactory->createInstance("ooo.vba.VBAGlobals"); + } + catch(const Exception& ) + { + } +} + +void SAL_CALL SfxLibraryContainer::setProjectName( const OUString& _projectname ) +{ + msProjectName = _projectname; + BasicManager* pBasMgr = getBasicManager(); + // Temporary HACK + // Some parts of the VBA handling ( e.g. in core basic ) + // code expect the name of the VBA project to be set as the name of + // the basic manager. Provide fail back here. + if( pBasMgr ) + { + pBasMgr->SetName( msProjectName ); + } +} + +sal_Int32 SAL_CALL SfxLibraryContainer::getRunningVBAScripts() +{ + LibraryContainerMethodGuard aGuard( *this ); + return mnRunningVBAScripts; +} + +void SAL_CALL SfxLibraryContainer::addVBAScriptListener( const Reference< vba::XVBAScriptListener >& rxListener ) +{ + maVBAScriptListeners.addInterface( rxListener ); +} + +void SAL_CALL SfxLibraryContainer::removeVBAScriptListener( const Reference< vba::XVBAScriptListener >& rxListener ) +{ + maVBAScriptListeners.removeInterface( rxListener ); +} + +void SAL_CALL SfxLibraryContainer::broadcastVBAScriptEvent( sal_Int32 nIdentifier, const OUString& rModuleName ) +{ + // own lock for accessing the number of running scripts + enterMethod(); + switch( nIdentifier ) + { + case vba::VBAScriptEventId::SCRIPT_STARTED: + ++mnRunningVBAScripts; + break; + case vba::VBAScriptEventId::SCRIPT_STOPPED: + --mnRunningVBAScripts; + break; + } + leaveMethod(); + + Reference< XModel > xModel = mxOwnerDocument; // weak-ref -> ref + vba::VBAScriptEvent aEvent( Reference<XInterface>(xModel, UNO_QUERY), nIdentifier, rModuleName ); + maVBAScriptListeners.notifyEach( &css::script::vba::XVBAScriptListener::notifyVBAScriptEvent, aEvent ); +} + +// Methods XPropertySet +css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL SfxLibraryContainer::getPropertySetInfo() +{ + return uno::Reference<beans::XPropertySetInfo>(); +} + +void SAL_CALL SfxLibraryContainer::setPropertyValue(const OUString& aPropertyName, + const uno::Any& aValue) +{ + if (aPropertyName != sVBATextEncodingPropName) + throw UnknownPropertyException(aPropertyName, static_cast<uno::XWeak*>(this)); + aValue >>= meVBATextEncoding; +} + +css::uno::Any SAL_CALL SfxLibraryContainer::getPropertyValue(const OUString& aPropertyName) +{ + if (aPropertyName == sVBATextEncodingPropName) + return uno::Any(meVBATextEncoding); + throw UnknownPropertyException(aPropertyName, static_cast<uno::XWeak*>(this)); +} + +void SAL_CALL SfxLibraryContainer::addPropertyChangeListener( + const OUString& /* aPropertyName */, const Reference<XPropertyChangeListener>& /* xListener */) +{ + throw NoSupportException(); +} + +void SAL_CALL SfxLibraryContainer::removePropertyChangeListener( + const OUString& /* aPropertyName */, const Reference<XPropertyChangeListener>& /* aListener */) +{ + throw NoSupportException(); +} + +void SAL_CALL SfxLibraryContainer::addVetoableChangeListener( + const OUString& /* PropertyName */, const Reference<XVetoableChangeListener>& /* aListener */) +{ + throw NoSupportException(); +} + +void SAL_CALL SfxLibraryContainer::removeVetoableChangeListener( + const OUString& /* PropertyName */, const Reference<XVetoableChangeListener>& /* aListener */) +{ + throw NoSupportException(); +} + +// Methods XServiceInfo +sal_Bool SAL_CALL SfxLibraryContainer::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +// Implementation class SfxLibrary + +// Ctor +SfxLibrary::SfxLibrary( ModifiableHelper& _rModifiable, const Type& aType, + const Reference< XSimpleFileAccess3 >& xSFI ) + : OComponentHelper( m_aMutex ) + , mxSFI( xSFI ) + , mrModifiable( _rModifiable ) + , maNameContainer( new NameContainer(aType) ) + , mbLoaded( true ) + , mbIsModified( true ) + , mbInitialised( false ) + , mbLink( false ) + , mbReadOnly( false ) + , mbReadOnlyLink( false ) + , mbPreload( false ) + , mbPasswordProtected( false ) + , mbPasswordVerified( false ) + , mbDoc50Password( false ) + , mbSharedIndexFile( false ) + , mbExtension( false ) +{ +} + +SfxLibrary::SfxLibrary( ModifiableHelper& _rModifiable, const Type& aType, + const Reference< XSimpleFileAccess3 >& xSFI, + OUString aLibInfoFileURL, OUString aStorageURL, bool ReadOnly ) + : OComponentHelper( m_aMutex ) + , mxSFI( xSFI ) + , mrModifiable( _rModifiable ) + , maNameContainer( new NameContainer(aType) ) + , mbLoaded( false ) + , mbIsModified( true ) + , mbInitialised( false ) + , maLibInfoFileURL(std::move( aLibInfoFileURL )) + , maStorageURL(std::move( aStorageURL )) + , mbLink( true ) + , mbReadOnly( false ) + , mbReadOnlyLink( ReadOnly ) + , mbPreload( false ) + , mbPasswordProtected( false ) + , mbPasswordVerified( false ) + , mbDoc50Password( false ) + , mbSharedIndexFile( false ) + , mbExtension( false ) +{ +} + +bool SfxLibrary::isLoadedStorable() +{ + return mbLoaded && (!mbPasswordProtected || mbPasswordVerified); +} + +void SfxLibrary::implSetModified( bool _bIsModified ) +{ + if ( mbIsModified == _bIsModified ) + { + return; + } + mbIsModified = _bIsModified; + if ( mbIsModified ) + { + mrModifiable.setModified( true ); + } +} + +// Methods XInterface +Any SAL_CALL SfxLibrary::queryInterface( const Type& rType ) +{ + Any aRet = + ::cppu::queryInterface( + rType, + static_cast< XContainer * >( this ), + static_cast< XNameContainer * >( this ), + static_cast< XNameAccess * >( this ), + static_cast< XElementAccess * >( this ), + static_cast< XChangesNotifier * >( this ) ); + if( !aRet.hasValue() ) + { + aRet = OComponentHelper::queryInterface( rType ); + } + return aRet; +} + +// Methods XElementAccess +Type SfxLibrary::getElementType() +{ + return maNameContainer->getElementType(); +} + +sal_Bool SfxLibrary::hasElements() +{ + bool bRet = maNameContainer->hasElements(); + return bRet; +} + +// Methods XNameAccess +Any SfxLibrary::getByName( const OUString& aName ) +{ + impl_checkLoaded(); + + Any aRetAny = maNameContainer->getByName( aName ) ; + return aRetAny; +} + +Sequence< OUString > SfxLibrary::getElementNames() +{ + return maNameContainer->getElementNames(); +} + +sal_Bool SfxLibrary::hasByName( const OUString& aName ) +{ + bool bRet = maNameContainer->hasByName( aName ); + return bRet; +} + +void SfxLibrary::impl_checkReadOnly() +{ + if( mbReadOnly || (mbLink && mbReadOnlyLink) ) + { + throw IllegalArgumentException( + "Library is readonly.", + // TODO: resource + *this, 0 + ); + } +} + +void SfxLibrary::impl_checkLoaded() +{ + if ( !mbLoaded ) + { + throw WrappedTargetException( + OUString(), + *this, + Any( LibraryNotLoadedException( + OUString(), + *this + ) ) + ); + } +} + +// Methods XNameReplace +void SfxLibrary::replaceByName( const OUString& aName, const Any& aElement ) +{ + impl_checkReadOnly(); + impl_checkLoaded(); + + SAL_WARN_IF( + !isLibraryElementValid(aElement), "basic", + "SfxLibrary::replaceByName: replacing element is invalid!"); + + maNameContainer->replaceByName( aName, aElement ); + implSetModified( true ); +} + + +// Methods XNameContainer +void SfxLibrary::insertByName( const OUString& aName, const Any& aElement ) +{ + impl_checkReadOnly(); + impl_checkLoaded(); + + SAL_WARN_IF( + !isLibraryElementValid(aElement), "basic", + "SfxLibrary::insertByName: to-be-inserted element is invalid!"); + + maNameContainer->insertByName( aName, aElement ); + implSetModified( true ); +} + +void SfxLibrary::impl_removeWithoutChecks( const OUString& _rElementName ) +{ + maNameContainer->removeByName( _rElementName ); + implSetModified( true ); + + // Remove element file + if( maStorageURL.isEmpty() ) + return; + + INetURLObject aElementInetObj( maStorageURL ); + aElementInetObj.insertName( _rElementName, false, + INetURLObject::LAST_SEGMENT, + INetURLObject::EncodeMechanism::All ); + aElementInetObj.setExtension( maLibElementFileExtension ); + OUString aFile = aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + try + { + if( mxSFI->exists( aFile ) ) + { + mxSFI->kill( aFile ); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + } +} + +void SfxLibrary::removeByName( const OUString& Name ) +{ + impl_checkReadOnly(); + impl_checkLoaded(); + impl_removeWithoutChecks( Name ); +} + +// XTypeProvider +Sequence< Type > SfxLibrary::getTypes() +{ + static OTypeCollection ourTypes_NameContainer( + cppu::UnoType<XNameContainer>::get(), + cppu::UnoType<XContainer>::get(), + cppu::UnoType<XChangesNotifier>::get(), + OComponentHelper::getTypes() ); + + return ourTypes_NameContainer.getTypes(); +} + + +Sequence< sal_Int8 > SfxLibrary::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// Methods XContainer +void SAL_CALL SfxLibrary::addContainerListener( const Reference< XContainerListener >& xListener ) +{ + maNameContainer->setEventSource( static_cast< XInterface* >( static_cast<OWeakObject*>(this) ) ); + maNameContainer->addContainerListener( xListener ); +} + +void SAL_CALL SfxLibrary::removeContainerListener( const Reference< XContainerListener >& xListener ) +{ + maNameContainer->removeContainerListener( xListener ); +} + +// Methods XChangesNotifier +void SAL_CALL SfxLibrary::addChangesListener( const Reference< XChangesListener >& xListener ) +{ + maNameContainer->setEventSource( static_cast< XInterface* >( static_cast<OWeakObject*>(this) ) ); + maNameContainer->addChangesListener( xListener ); +} + +void SAL_CALL SfxLibrary::removeChangesListener( const Reference< XChangesListener >& xListener ) +{ + maNameContainer->removeChangesListener( xListener ); +} + + +// Implementation class ScriptExtensionIterator + +constexpr OUStringLiteral sBasicLibMediaType = u"application/vnd.sun.star.basic-library"; +constexpr OUStringLiteral sDialogLibMediaType = u"application/vnd.sun.star.dialog-library"; + +ScriptExtensionIterator::ScriptExtensionIterator() + : m_xContext( comphelper::getProcessComponentContext() ) + , m_eState( USER_EXTENSIONS ) + , m_bUserPackagesLoaded( false ) + , m_bSharedPackagesLoaded( false ) + , m_bBundledPackagesLoaded( false ) + , m_iUserPackage( 0 ) + , m_iSharedPackage( 0 ) + , m_iBundledPackage( 0 ) + , m_pScriptSubPackageIterator( nullptr ) +{} + +OUString ScriptExtensionIterator::nextBasicOrDialogLibrary( bool& rbPureDialogLib ) +{ + OUString aRetLib; + + while( aRetLib.isEmpty() && m_eState != END_REACHED ) + { + switch( m_eState ) + { + case USER_EXTENSIONS: + { + Reference< deployment::XPackage > xScriptPackage = + implGetNextUserScriptPackage( rbPureDialogLib ); + if( !xScriptPackage.is() ) + { + break; + } + aRetLib = xScriptPackage->getURL(); + break; + } + + case SHARED_EXTENSIONS: + { + Reference< deployment::XPackage > xScriptPackage = + implGetNextSharedScriptPackage( rbPureDialogLib ); + if( !xScriptPackage.is() ) + { + break; + } + aRetLib = xScriptPackage->getURL(); + break; + } + case BUNDLED_EXTENSIONS: + { + Reference< deployment::XPackage > xScriptPackage = + implGetNextBundledScriptPackage( rbPureDialogLib ); + if( !xScriptPackage.is() ) + { + break; + } + aRetLib = xScriptPackage->getURL(); + break; + } + case END_REACHED: + SAL_WARN( + "basic", + ("ScriptExtensionIterator::nextBasicOrDialogLibrary():" + " Invalid case END_REACHED")); + break; + } + } + + return aRetLib; +} + +ScriptSubPackageIterator::ScriptSubPackageIterator( Reference< deployment::XPackage > const & xMainPackage ) + : m_xMainPackage( xMainPackage ) + , m_bIsValid( false ) + , m_bIsBundle( false ) + , m_nSubPkgCount( 0 ) + , m_iNextSubPkg( 0 ) +{ + if( !m_xMainPackage.is() ) + { + return; + } + // Check if parent package is registered + beans::Optional< beans::Ambiguous<sal_Bool> > option( m_xMainPackage->isRegistered + ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) ); + bool bRegistered = false; + if( option.IsPresent ) + { + beans::Ambiguous<sal_Bool> const & reg = option.Value; + if( !reg.IsAmbiguous && reg.Value ) + { + bRegistered = true; + } + } + if( bRegistered ) + { + m_bIsValid = true; + if( m_xMainPackage->isBundle() ) + { + m_bIsBundle = true; + m_aSubPkgSeq = m_xMainPackage->getBundle( Reference<task::XAbortChannel>(), + Reference<ucb::XCommandEnvironment>() ); + m_nSubPkgCount = m_aSubPkgSeq.getLength(); + } + } +} + +Reference< deployment::XPackage > ScriptSubPackageIterator::getNextScriptSubPackage( bool& rbPureDialogLib ) +{ + rbPureDialogLib = false; + + Reference< deployment::XPackage > xScriptPackage; + if( !m_bIsValid ) + { + return xScriptPackage; + } + if( m_bIsBundle ) + { + const Reference< deployment::XPackage >* pSeq = m_aSubPkgSeq.getConstArray(); + sal_Int32 iPkg; + for( iPkg = m_iNextSubPkg ; iPkg < m_nSubPkgCount ; ++iPkg ) + { + const Reference< deployment::XPackage > xSubPkg = pSeq[ iPkg ]; + xScriptPackage = implDetectScriptPackage( xSubPkg, rbPureDialogLib ); + if( xScriptPackage.is() ) + { + break; + } + } + m_iNextSubPkg = iPkg + 1; + } + else + { + xScriptPackage = implDetectScriptPackage( m_xMainPackage, rbPureDialogLib ); + m_bIsValid = false; // No more script packages + } + + return xScriptPackage; +} + +Reference< deployment::XPackage > ScriptSubPackageIterator::implDetectScriptPackage ( const Reference< deployment::XPackage >& rPackage, + bool& rbPureDialogLib ) +{ + Reference< deployment::XPackage > xScriptPackage; + + if( rPackage.is() ) + { + const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = rPackage->getPackageType(); + OUString aMediaType = xPackageTypeInfo->getMediaType(); + if ( aMediaType == sBasicLibMediaType ) + { + xScriptPackage = rPackage; + } + else if ( aMediaType == sDialogLibMediaType ) + { + rbPureDialogLib = true; + xScriptPackage = rPackage; + } + } + + return xScriptPackage; +} + +Reference< deployment::XPackage > ScriptExtensionIterator::implGetNextUserScriptPackage( bool& rbPureDialogLib ) +{ + Reference< deployment::XPackage > xScriptPackage; + + if( !m_bUserPackagesLoaded ) + { + try + { + Reference< XExtensionManager > xManager = ExtensionManager::get( m_xContext ); + m_aUserPackagesSeq = xManager->getDeployedExtensions("user", + Reference< task::XAbortChannel >(), + Reference< ucb::XCommandEnvironment >() ); + } + catch(const css::uno::DeploymentException& ) + { + // Special Office installations may not contain deployment code + m_eState = END_REACHED; + return xScriptPackage; + } + + m_bUserPackagesLoaded = true; + } + + if( m_iUserPackage == m_aUserPackagesSeq.getLength() ) + { + m_eState = SHARED_EXTENSIONS; // Later: SHARED_MODULE + } + else + { + if( m_pScriptSubPackageIterator == nullptr ) + { + const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray(); + Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage ]; + SAL_WARN_IF( + !xPackage.is(), "basic", + ("ScriptExtensionIterator::implGetNextUserScriptPackage():" + " Invalid package")); + m_pScriptSubPackageIterator = new ScriptSubPackageIterator( xPackage ); + } + + xScriptPackage = m_pScriptSubPackageIterator->getNextScriptSubPackage( rbPureDialogLib ); + if( !xScriptPackage.is() ) + { + delete m_pScriptSubPackageIterator; + m_pScriptSubPackageIterator = nullptr; + m_iUserPackage++; + } + } + + return xScriptPackage; +} + +Reference< deployment::XPackage > ScriptExtensionIterator::implGetNextSharedScriptPackage( bool& rbPureDialogLib ) +{ + Reference< deployment::XPackage > xScriptPackage; + + if( !m_bSharedPackagesLoaded ) + { + try + { + Reference< XExtensionManager > xSharedManager = ExtensionManager::get( m_xContext ); + m_aSharedPackagesSeq = xSharedManager->getDeployedExtensions("shared", + Reference< task::XAbortChannel >(), + Reference< ucb::XCommandEnvironment >() ); + } + catch(const css::uno::DeploymentException& ) + { + // Special Office installations may not contain deployment code + return xScriptPackage; + } + + m_bSharedPackagesLoaded = true; + } + + if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() ) + { + m_eState = BUNDLED_EXTENSIONS; + } + else + { + if( m_pScriptSubPackageIterator == nullptr ) + { + const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray(); + Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage ]; + SAL_WARN_IF( + !xPackage.is(), "basic", + ("ScriptExtensionIterator::implGetNextSharedScriptPackage():" + " Invalid package")); + m_pScriptSubPackageIterator = new ScriptSubPackageIterator( xPackage ); + } + + xScriptPackage = m_pScriptSubPackageIterator->getNextScriptSubPackage( rbPureDialogLib ); + if( !xScriptPackage.is() ) + { + delete m_pScriptSubPackageIterator; + m_pScriptSubPackageIterator = nullptr; + m_iSharedPackage++; + } + } + + return xScriptPackage; +} + +Reference< deployment::XPackage > ScriptExtensionIterator::implGetNextBundledScriptPackage( bool& rbPureDialogLib ) +{ + Reference< deployment::XPackage > xScriptPackage; + + if( !m_bBundledPackagesLoaded ) + { + try + { + Reference< XExtensionManager > xManager = ExtensionManager::get( m_xContext ); + m_aBundledPackagesSeq = xManager->getDeployedExtensions("bundled", + Reference< task::XAbortChannel >(), + Reference< ucb::XCommandEnvironment >() ); + } + catch(const css::uno::DeploymentException& ) + { + // Special Office installations may not contain deployment code + return xScriptPackage; + } + + m_bBundledPackagesLoaded = true; + } + + if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() ) + { + m_eState = END_REACHED; + } + else + { + if( m_pScriptSubPackageIterator == nullptr ) + { + const Reference< deployment::XPackage >* pBundledPackages = m_aBundledPackagesSeq.getConstArray(); + Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage ]; + SAL_WARN_IF( + !xPackage.is(), "basic", + ("ScriptExtensionIterator::implGetNextBundledScriptPackage():" + " Invalid package")); + m_pScriptSubPackageIterator = new ScriptSubPackageIterator( xPackage ); + } + + xScriptPackage = m_pScriptSubPackageIterator->getNextScriptSubPackage( rbPureDialogLib ); + if( !xScriptPackage.is() ) + { + delete m_pScriptSubPackageIterator; + m_pScriptSubPackageIterator = nullptr; + m_iBundledPackage++; + } + } + + return xScriptPackage; +} + +} // namespace basic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |