diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /basic/source/basmgr | |
parent | Initial commit. (diff) | |
download | libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.tar.xz libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/basmgr')
-rw-r--r-- | basic/source/basmgr/basicmanagerrepository.cxx | 626 | ||||
-rw-r--r-- | basic/source/basmgr/basmgr.cxx | 2120 | ||||
-rw-r--r-- | basic/source/basmgr/vbahelper.cxx | 187 |
3 files changed, 2933 insertions, 0 deletions
diff --git a/basic/source/basmgr/basicmanagerrepository.cxx b/basic/source/basmgr/basicmanagerrepository.cxx new file mode 100644 index 000000000..aed39f9f2 --- /dev/null +++ b/basic/source/basmgr/basicmanagerrepository.cxx @@ -0,0 +1,626 @@ +/* -*- 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 <basic/basicmanagerrepository.hxx> +#include <basic/basmgr.hxx> +#include <scriptcont.hxx> +#include <dlgcont.hxx> +#include <sbintern.hxx> +#include <sbxbase.hxx> + +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <o3tl/string_view.hxx> +#include <svtools/ehdl.hxx> +#include <svtools/sfxecode.hxx> +#include <unotools/pathoptions.hxx> +#include <svl/hint.hxx> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/documentinfo.hxx> +#include <unotools/eventlisteneradapter.hxx> + +#include <sot/storage.hxx> + +#include <map> +#include <mutex> + + +namespace basic +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::frame::Desktop; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::embed::XStorage; + using ::com::sun::star::script::XPersistentLibraryContainer; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::document::XStorageBasedDocument; + using ::com::sun::star::document::XEmbeddedScripts; + + typedef std::map< Reference< XInterface >, std::unique_ptr<BasicManager> > BasicManagerStore; + + typedef std::vector< BasicManagerCreationListener* > CreationListeners; + + class ImplRepository : public ::utl::OEventListenerAdapter, public SfxListener, public SvRefBase + { + private: + ImplRepository(); + ~ImplRepository(); + + private: + BasicManagerStore m_aStore; + CreationListeners m_aCreationListeners; + + public: + static ImplRepository& Instance(); + + BasicManager* getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel ); + BasicManager* getOrCreateApplicationBasicManager(); + static BasicManager* getApplicationBasicManager(); + static void setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager ); + void registerCreationListener( BasicManagerCreationListener& _rListener ); + void revokeCreationListener( BasicManagerCreationListener& _rListener ); + + private: + /** retrieves the location at which the BasicManager for the given model + is stored. + + If previously, the BasicManager for this model has never been requested, + then the model is added to the map, with an initial NULL BasicManager. + + @param _rxDocumentModel + the model whose BasicManager's location is to be retrieved. Must not be <NULL/>. + + @precond + our mutex is locked + */ + BasicManagerStore::iterator + impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel ); + + /** tests if there is a location set at which the BasicManager for the given model + is stored. + + @param _rxDocumentModel + the model whose BasicManager's location is to be retrieved. Must not be <NULL/>. + + @precond + our mutex is locked + */ + bool impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const; + + /** creates a new BasicManager instance for the given model + + @param _out_rpBasicManager + reference to the pointer variable that will hold the new + BasicManager. + + @param _rxDocumentModel + the model whose BasicManager will be created. Must not be <NULL/>. + */ + bool impl_createManagerForModel( + BasicManagerStore::iterator location, + const Reference< XModel >& _rxDocumentModel ); + + /** creates the application-wide BasicManager + */ + BasicManager* impl_createApplicationBasicManager(); + + /** notifies all listeners which expressed interest in the creation of BasicManager instances. + */ + void impl_notifyCreationListeners( + const Reference< XModel >& _rxDocumentModel, + BasicManager& _rManager + ); + + /** retrieves the current storage of a given document + + @param _rxDocument + the document whose storage is to be retrieved. + + @param _out_rStorage + takes the storage upon successful return. Note that this might be <NULL/> even + if <TRUE/> is returned. In this case, the document has not yet been saved. + + @return + <TRUE/> if the storage could be successfully retrieved (in which case + <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise. + In the latter case, processing this document should stop. + */ + static bool impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage ); + + /** retrieves the containers for Basic and Dialog libraries for a given document + + @param _rxDocument + the document whose containers are to be retrieved. + + @param _out_rxBasicLibraries + takes the basic library container upon successful return + + @param _out_rxDialogLibraries + takes the dialog library container upon successful return + + @return + <TRUE/> if and only if both containers exist, and could successfully be retrieved + */ + static bool impl_getDocumentLibraryContainers_nothrow( + const Reference< XModel >& _rxDocument, + Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries, + Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries + ); + + /** initializes the given library containers, which belong to a document + */ + static void impl_initDocLibraryContainers_nothrow( + const Reference< XPersistentLibraryContainer >& _rxBasicLibraries, + const Reference< XPersistentLibraryContainer >& _rxDialogLibraries + ); + + // OEventListenerAdapter overridables + virtual void _disposing( const css::lang::EventObject& _rSource ) override; + + // SfxListener overridables + virtual void Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) override; + + /** removes the Model/BasicManager pair given by iterator from our store + */ + void impl_removeFromRepository( const BasicManagerStore::iterator& _pos ); + + private: + StarBASIC* impl_getDefaultAppBasicLibrary(); + }; + + ImplRepository::ImplRepository() + { + } + + ImplRepository::~ImplRepository() + { + // Avoid double-delete of managers when they are destroyed in our dtor, and start notify us + for (auto& it : m_aStore) + EndListening(*it.second); + } + + ImplRepository& ImplRepository::Instance() + { + tools::SvRef<SvRefBase>& repository = GetSbxData_Impl().mrImplRepository; + { + static std::mutex aMutex; + std::unique_lock aGuard(aMutex); + if (!repository) + repository = new ImplRepository; + } + return *static_cast<ImplRepository*>(repository.get()); + } + + BasicManager* ImplRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel ) + { + SolarMutexGuard g; + + /* #163556# (DR) - This function may be called recursively while + constructing the Basic manager and loading the Basic storage. By + passing the map entry received from impl_getLocationForModel() to + the function impl_createManagerForModel(), the new Basic manager + will be put immediately into the map of existing Basic managers, + thus a recursive call of this function will find and return it + without creating another instance. + */ + auto const loc = impl_getLocationForModel( _rxDocumentModel ); + if (loc->second != nullptr) + return loc->second.get(); + if (impl_createManagerForModel(loc, _rxDocumentModel)) + return loc->second.get(); + return nullptr; + } + + BasicManager* ImplRepository::getOrCreateApplicationBasicManager() + { + SolarMutexGuard g; + + BasicManager* pAppManager = GetSbData()->pAppBasMgr.get(); + if (pAppManager == nullptr) + pAppManager = impl_createApplicationBasicManager(); + return pAppManager; + } + + BasicManager* ImplRepository::getApplicationBasicManager() + { + SolarMutexGuard g; + + return GetSbData()->pAppBasMgr.get(); + } + + void ImplRepository::setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager ) + { + SolarMutexGuard g; + + GetSbData()->pAppBasMgr = std::move(_pBasicManager); + } + + + BasicManager* ImplRepository::impl_createApplicationBasicManager() + { + SolarMutexGuard g; + + OSL_PRECOND(getApplicationBasicManager() == nullptr, "ImplRepository::impl_createApplicationBasicManager: there already is one!"); + + // Determine Directory + SvtPathOptions aPathCFG; + OUString aAppBasicDir( aPathCFG.GetBasicPath() ); + if ( aAppBasicDir.isEmpty() ) + { + aPathCFG.SetBasicPath("$(prog)"); + } + + // Create basic and load it + // AppBasicDir is now a PATH + INetURLObject aAppBasic( SvtPathOptions().SubstituteVariable("$(progurl)") ); + aAppBasic.insertName( Application::GetAppName() ); + + BasicManager* pBasicManager = new BasicManager( new StarBASIC, &aAppBasicDir ); + setApplicationBasicManager( std::unique_ptr<BasicManager>(pBasicManager) ); + + // The first dir in the path as destination: + OUString aFileName( aAppBasic.getName() ); + aAppBasic = INetURLObject( o3tl::getToken(aAppBasicDir, 1, ';') ); + DBG_ASSERT(aAppBasic.GetProtocol() != INetProtocol::NotValid, + OString("Invalid URL: \"" + + OUStringToOString(aAppBasicDir, osl_getThreadTextEncoding()) + + "\"").getStr()); + aAppBasic.insertName( aFileName ); + pBasicManager->SetStorageName( aAppBasic.PathToFileName() ); + + // Basic container + rtl::Reference<SfxScriptLibraryContainer> pBasicCont = new SfxScriptLibraryContainer( Reference< XStorage >() ); + pBasicCont->setBasicManager( pBasicManager ); + + // Dialog container + rtl::Reference<SfxDialogLibraryContainer> pDialogCont = new SfxDialogLibraryContainer( Reference< XStorage >() ); + + LibraryContainerInfo aInfo( pBasicCont, pDialogCont, static_cast< OldBasicPassword* >( pBasicCont.get() ) ); + pBasicManager->SetLibraryContainerInfo( aInfo ); + + // global constants + + // StarDesktop + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + pBasicManager->SetGlobalUNOConstant( "StarDesktop", css::uno::Any( Desktop::create(xContext))); + + // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo) + + // notify + impl_notifyCreationListeners( nullptr, *pBasicManager ); + + // outta here + return pBasicManager; + } + + + void ImplRepository::registerCreationListener( BasicManagerCreationListener& _rListener ) + { + SolarMutexGuard g; + + m_aCreationListeners.push_back( &_rListener ); + } + + + void ImplRepository::revokeCreationListener( BasicManagerCreationListener& _rListener ) + { + SolarMutexGuard g; + + CreationListeners::iterator pos = std::find( m_aCreationListeners.begin(), m_aCreationListeners.end(), &_rListener ); + if ( pos != m_aCreationListeners.end() ) + m_aCreationListeners.erase( pos ); + else { + OSL_FAIL( "ImplRepository::revokeCreationListener: listener is not registered!" ); + } + } + + + void ImplRepository::impl_notifyCreationListeners( const Reference< XModel >& _rxDocumentModel, BasicManager& _rManager ) + { + for (auto const& creationListener : m_aCreationListeners) + { + creationListener->onBasicManagerCreated( _rxDocumentModel, _rManager ); + } + } + + + StarBASIC* ImplRepository::impl_getDefaultAppBasicLibrary() + { + BasicManager* pAppManager = getOrCreateApplicationBasicManager(); + + StarBASIC* pAppBasic = pAppManager ? pAppManager->GetLib(0) : nullptr; + DBG_ASSERT( pAppBasic != nullptr, "impl_getApplicationBasic: unable to determine the default application's Basic library!" ); + return pAppBasic; + } + + BasicManagerStore::iterator ImplRepository::impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel ) + { + Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY ); + DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" ); + + return m_aStore.try_emplace(xNormalized).first; + } + + bool ImplRepository::impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const + { + Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY ); + DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" ); + + return m_aStore.find(xNormalized) != m_aStore.end(); + } + + void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference< XPersistentLibraryContainer >& _rxBasicLibraries, const Reference< XPersistentLibraryContainer >& _rxDialogLibraries ) + { + OSL_PRECOND( _rxBasicLibraries.is() && _rxDialogLibraries.is(), + "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" ); + + try + { + // ensure there's a standard library in the basic container + static constexpr OUStringLiteral aStdLibName( u"Standard" ); + if ( !_rxBasicLibraries->hasByName( aStdLibName ) ) + { + _rxBasicLibraries->createLibrary( aStdLibName ); + } + // as well as in the dialog container + if ( !_rxDialogLibraries->hasByName( aStdLibName ) ) + { + _rxDialogLibraries->createLibrary( aStdLibName ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + } + } + + bool ImplRepository::impl_createManagerForModel( BasicManagerStore::iterator location, const Reference< XModel >& _rxDocumentModel ) + { + auto & _out_rpBasicManager = location->second; + + StarBASIC* pAppBasic = impl_getDefaultAppBasicLibrary(); + + _out_rpBasicManager = nullptr; + Reference< XStorage > xStorage; + if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel, xStorage ) ) + { + m_aStore.erase(location); + // the document is not able to provide the storage it is based on. + return false; + } + Reference< XPersistentLibraryContainer > xBasicLibs; + Reference< XPersistentLibraryContainer > xDialogLibs; + if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel, xBasicLibs, xDialogLibs ) ) + { + m_aStore.erase(location); + // the document does not have BasicLibraries and DialogLibraries + return false; + } + + if ( xStorage.is() ) + { + // load BASIC-manager + SfxErrorContext aErrContext( ERRCTX_SFX_LOADBASIC, + ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel ) ); + OUString aAppBasicDir = SvtPathOptions().GetBasicPath(); + + // Storage and BaseURL are only needed by binary documents! + tools::SvRef<SotStorage> xDummyStor = new SotStorage( OUString() ); + _out_rpBasicManager.reset(new BasicManager( *xDummyStor, u"" /* TODO/LATER: xStorage */, + pAppBasic, + &aAppBasicDir, true )); + if ( !_out_rpBasicManager->GetErrors().empty() ) + { + // handle errors + std::vector<BasicError>& aErrors = _out_rpBasicManager->GetErrors(); + for(const auto& rError : aErrors) + { + // show message to user + if ( ErrorHandler::HandleError( rError.GetErrorId() ) == DialogMask::ButtonsCancel ) + { + // user wants to break loading of BASIC-manager + _out_rpBasicManager.reset(); + xStorage.clear(); + break; + } + } + } + } + + // not loaded? + if ( !xStorage.is() ) + { + // create new BASIC-manager + StarBASIC* pBasic = new StarBASIC( pAppBasic ); + pBasic->SetFlag( SbxFlagBits::ExtSearch ); + _out_rpBasicManager.reset(new BasicManager( pBasic, nullptr, true )); + } + + // knit the containers with the BasicManager + LibraryContainerInfo aInfo( xBasicLibs, xDialogLibs, dynamic_cast< OldBasicPassword* >( xBasicLibs.get() ) ); + OSL_ENSURE( aInfo.mpOldBasicPassword, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" ); + _out_rpBasicManager->SetLibraryContainerInfo( aInfo ); + + // initialize the containers + impl_initDocLibraryContainers_nothrow( xBasicLibs, xDialogLibs ); + + // so that also dialogs etc. could be 'qualified' addressed + _out_rpBasicManager->GetLib(0)->SetParent( pAppBasic ); + + // global properties in the document's Basic + _out_rpBasicManager->SetGlobalUNOConstant( "ThisComponent", css::uno::Any( _rxDocumentModel ) ); + + // notify + impl_notifyCreationListeners( _rxDocumentModel, *_out_rpBasicManager ); + + // register as listener for this model being disposed/closed + OSL_ENSURE( _rxDocumentModel.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" ); + assert(impl_hasLocationForModel(_rxDocumentModel)); + startComponentListening( _rxDocumentModel ); + + bool bOk = false; + // startComponentListening may fail in a disposed _rxDocumentModel, in which case _out_rpBasicManager will be removed + // from the map and destroyed + if (impl_hasLocationForModel(_rxDocumentModel)) + { + bOk = true; + // register as listener for the BasicManager being destroyed + StartListening( *_out_rpBasicManager ); + } + + // #i104876: Library container must not be modified just after + // creation. This happens as side effect when creating default + // "Standard" libraries and needs to be corrected here + xBasicLibs->setModified( false ); + xDialogLibs->setModified( false ); + return bOk; + } + + bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage ) + { + _out_rStorage.clear(); + try + { + Reference< XStorageBasedDocument > xStorDoc( _rxDocument, UNO_QUERY_THROW ); + _out_rStorage.set( xStorDoc->getDocumentStorage() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + return false; + } + return true; + } + + + bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference< XModel >& _rxDocument, + Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries, Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries ) + { + _out_rxBasicLibraries.clear(); + _out_rxDialogLibraries.clear(); + try + { + Reference< XEmbeddedScripts > xScripts( _rxDocument, UNO_QUERY_THROW ); + _out_rxBasicLibraries.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW ); + _out_rxDialogLibraries.set( xScripts->getDialogLibraries(), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + } + return _out_rxBasicLibraries.is() && _out_rxDialogLibraries.is(); + } + + + void ImplRepository::impl_removeFromRepository( const BasicManagerStore::iterator& _pos ) + { + OSL_PRECOND( _pos != m_aStore.end(), "ImplRepository::impl_removeFromRepository: invalid position!" ); + + std::unique_ptr<BasicManager> pManager = std::move(_pos->second); + Reference<XModel> xModel(_pos->first, UNO_QUERY); + + // *first* remove from map (else Notify won't work properly) + m_aStore.erase( _pos ); + + EndListening( *pManager ); + + assert(xModel.is()); + if (xModel.is()) + stopComponentListening(xModel); + } + + + void ImplRepository::_disposing( const css::lang::EventObject& _rSource ) + { + SolarMutexGuard g; + + Reference< XInterface > xNormalizedSource( _rSource.Source, UNO_QUERY ); + + BasicManagerStore::iterator it = std::find_if(m_aStore.begin(), m_aStore.end(), + [&xNormalizedSource](BasicManagerStore::reference rEntry) { + return rEntry.first.get() == xNormalizedSource.get(); }); + if (it != m_aStore.end()) + { + impl_removeFromRepository( it ); + return; + } + + OSL_FAIL( "ImplRepository::_disposing: where does this come from?" ); + } + + + void ImplRepository::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) + { + if ( _rHint.GetId() != SfxHintId::Dying ) + // not interested in + return; + + BasicManager* pManager = dynamic_cast< BasicManager* >( &_rBC ); + OSL_ENSURE( pManager, "ImplRepository::Notify: where does this come from?" ); + + BasicManagerStore::iterator it = std::find_if(m_aStore.begin(), m_aStore.end(), + [&pManager](BasicManagerStore::reference rEntry) { return rEntry.second.get() == pManager; }); + if (it != m_aStore.end()) + { + // a BasicManager which is still in our repository is being deleted. + // That's bad, since by definition, we *own* all instances in our + // repository. + OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" ); + m_aStore.erase( it ); + } + } + + BasicManager* BasicManagerRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel ) + { + return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel ); + } + + BasicManager* BasicManagerRepository::getApplicationBasicManager() + { + return ImplRepository::Instance().getOrCreateApplicationBasicManager(); + } + + void BasicManagerRepository::resetApplicationBasicManager() + { + ImplRepository::setApplicationBasicManager( nullptr ); + } + + void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener& _rListener ) + { + ImplRepository::Instance().registerCreationListener( _rListener ); + } + + void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener& _rListener ) + { + ImplRepository::Instance().revokeCreationListener( _rListener ); + } + +} // namespace basic + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/basmgr/basmgr.cxx b/basic/source/basmgr/basmgr.cxx new file mode 100644 index 000000000..e21b0560b --- /dev/null +++ b/basic/source/basmgr/basmgr.cxx @@ -0,0 +1,2120 @@ +/* -*- 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 <utility> +#include <vcl/errinf.hxx> +#include <tools/stream.hxx> +#include <sot/storage.hxx> +#include <tools/urlobj.hxx> +#include <svl/hint.hxx> +#include <basic/sbx.hxx> +#include <basic/sbmeth.hxx> +#include <sot/storinfo.hxx> +#include <unotools/pathoptions.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <basic/sbmod.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <basic/sberrors.hxx> +#include <basic/sbuno.hxx> +#include <basic/basmgr.hxx> +#include <global.hxx> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/XPersistentLibraryContainer.hpp> + +#include <memory> +#include <vector> + +#define LIB_SEP 0x01 +#define LIBINFO_SEP 0x02 +#define LIBINFO_ID 0x1491 +#define PASSWORD_MARKER 0x31452134 + + +// Library API, implemented for XML import/export + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/script/XStarBasicAccess.hpp> +#include <com/sun/star/script/XStarBasicModuleInfo.hpp> +#include <com/sun/star/script/XStarBasicDialogInfo.hpp> +#include <com/sun/star/script/XStarBasicLibraryInfo.hpp> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/script/ModuleInfo.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/script/vba/XVBAModuleInfo.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> + +#include <cppuhelper/implbase.hxx> + +using com::sun::star::uno::Reference; +using namespace com::sun::star; +using namespace com::sun::star::script; +using namespace cppu; + +typedef WeakImplHelper< container::XNameContainer > NameContainerHelper; +typedef WeakImplHelper< script::XStarBasicModuleInfo > ModuleInfoHelper; +typedef WeakImplHelper< script::XStarBasicAccess > StarBasicAccessHelper; + +// Version 1 +// sal_uInt32 nEndPos +// sal_uInt16 nId +// sal_uInt16 nVer +// bool bDoLoad +// String LibName +// String AbsStorageName +// String RelStorageName +// Version 2 +// + bool bReference + +constexpr OUStringLiteral szStdLibName = u"Standard"; +constexpr OUStringLiteral szBasicStorage = u"StarBASIC"; +constexpr OUStringLiteral szOldManagerStream = u"BasicManager"; +constexpr OUStringLiteral szManagerStream = u"BasicManager2"; +constexpr OUStringLiteral szImbedded = u"LIBIMBEDDED"; +constexpr OStringLiteral szCryptingKey = "CryptedBasic"; + + +const StreamMode eStreamReadMode = StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL; +const StreamMode eStorageReadMode = StreamMode::READ | StreamMode::SHARE_DENYWRITE; + +// BasMgrContainerListenerImpl + + +typedef ::cppu::WeakImplHelper< container::XContainerListener > ContainerListenerHelper; + +class BasMgrContainerListenerImpl: public ContainerListenerHelper +{ + BasicManager* mpMgr; + OUString maLibName; // empty -> no lib, but lib container + +public: + BasMgrContainerListenerImpl( BasicManager* pMgr, OUString aLibName ) + : mpMgr( pMgr ) + , maLibName(std::move( aLibName )) {} + + static void insertLibraryImpl( const uno::Reference< script::XLibraryContainer >& xScriptCont, BasicManager* pMgr, + const uno::Any& aLibAny, const OUString& aLibName ); + static void addLibraryModulesImpl( BasicManager const * pMgr, const uno::Reference< container::XNameAccess >& xLibNameAccess, + std::u16string_view aLibName ); + + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const container::ContainerEvent& Event ) override; +}; + + +// BasMgrContainerListenerImpl + + +void BasMgrContainerListenerImpl::insertLibraryImpl( const uno::Reference< script::XLibraryContainer >& xScriptCont, + BasicManager* pMgr, const uno::Any& aLibAny, const OUString& aLibName ) +{ + Reference< container::XNameAccess > xLibNameAccess; + aLibAny >>= xLibNameAccess; + + if( !pMgr->GetLib( aLibName ) ) + { + StarBASIC* pLib = + pMgr->CreateLibForLibContainer( aLibName, xScriptCont ); + DBG_ASSERT( pLib, "XML Import: Basic library could not be created"); + } + + uno::Reference< container::XContainer> xLibContainer( xLibNameAccess, uno::UNO_QUERY ); + if( xLibContainer.is() ) + { + // Register listener for library + Reference< container::XContainerListener > xLibraryListener + = new BasMgrContainerListenerImpl( pMgr, aLibName ); + xLibContainer->addContainerListener( xLibraryListener ); + } + + if( xScriptCont->isLibraryLoaded( aLibName ) ) + { + addLibraryModulesImpl( pMgr, xLibNameAccess, aLibName ); + } +} + + +void BasMgrContainerListenerImpl::addLibraryModulesImpl( BasicManager const * pMgr, + const uno::Reference< container::XNameAccess >& xLibNameAccess, std::u16string_view aLibName ) +{ + uno::Sequence< OUString > aModuleNames = xLibNameAccess->getElementNames(); + sal_Int32 nModuleCount = aModuleNames.getLength(); + + StarBASIC* pLib = pMgr->GetLib( aLibName ); + DBG_ASSERT( pLib, "BasMgrContainerListenerImpl::addLibraryModulesImpl: Unknown lib!"); + if( !pLib ) + return; + + const OUString* pNames = aModuleNames.getConstArray(); + for( sal_Int32 j = 0 ; j < nModuleCount ; j++ ) + { + OUString aModuleName = pNames[ j ]; + uno::Any aElement = xLibNameAccess->getByName( aModuleName ); + OUString aMod; + aElement >>= aMod; + uno::Reference< vba::XVBAModuleInfo > xVBAModuleInfo( xLibNameAccess, uno::UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( aModuleName ) ) + { + ModuleInfo aInfo = xVBAModuleInfo->getModuleInfo( aModuleName ); + pLib->MakeModule( aModuleName, aInfo, aMod ); + } + else + pLib->MakeModule( aModuleName, aMod ); + } + + pLib->SetModified( false ); +} + + +// XEventListener + + +void SAL_CALL BasMgrContainerListenerImpl::disposing( const lang::EventObject& ) {} + +// XContainerListener + + +void SAL_CALL BasMgrContainerListenerImpl::elementInserted( const container::ContainerEvent& Event ) +{ + bool bLibContainer = maLibName.isEmpty(); + OUString aName; + Event.Accessor >>= aName; + + if( bLibContainer ) + { + uno::Reference< script::XLibraryContainer > xScriptCont( Event.Source, uno::UNO_QUERY ); + if (xScriptCont.is()) + insertLibraryImpl(xScriptCont, mpMgr, Event.Element, aName); + StarBASIC* pLib = mpMgr->GetLib( aName ); + if ( pLib ) + { + uno::Reference< vba::XVBACompatibility > xVBACompat( xScriptCont, uno::UNO_QUERY ); + if ( xVBACompat.is() ) + pLib->SetVBAEnabled( xVBACompat->getVBACompatibilityMode() ); + } + } + else + { + + StarBASIC* pLib = mpMgr->GetLib( maLibName ); + DBG_ASSERT( pLib, "BasMgrContainerListenerImpl::elementInserted: Unknown lib!"); + if( pLib ) + { + SbModule* pMod = pLib->FindModule( aName ); + if( !pMod ) + { + OUString aMod; + Event.Element >>= aMod; + uno::Reference< vba::XVBAModuleInfo > xVBAModuleInfo( Event.Source, uno::UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( aName ) ) + { + ModuleInfo aInfo = xVBAModuleInfo->getModuleInfo( aName ); + pLib->MakeModule( aName, aInfo, aMod ); + } + else + pLib->MakeModule( aName, aMod ); + pLib->SetModified( false ); + } + } + } +} + + +void SAL_CALL BasMgrContainerListenerImpl::elementReplaced( const container::ContainerEvent& Event ) +{ + OUString aName; + Event.Accessor >>= aName; + + // Replace not possible for library container + DBG_ASSERT( !maLibName.isEmpty(), "library container fired elementReplaced()"); + + StarBASIC* pLib = mpMgr->GetLib( maLibName ); + if( !pLib ) + return; + + SbModule* pMod = pLib->FindModule( aName ); + OUString aMod; + Event.Element >>= aMod; + + if( pMod ) + pMod->SetSource32( aMod ); + else + pLib->MakeModule( aName, aMod ); + + pLib->SetModified( false ); +} + + +void SAL_CALL BasMgrContainerListenerImpl::elementRemoved( const container::ContainerEvent& Event ) +{ + OUString aName; + Event.Accessor >>= aName; + + bool bLibContainer = maLibName.isEmpty(); + if( bLibContainer ) + { + StarBASIC* pLib = mpMgr->GetLib( aName ); + if( pLib ) + { + sal_uInt16 nLibId = mpMgr->GetLibId( aName ); + mpMgr->RemoveLib( nLibId, false ); + } + } + else + { + StarBASIC* pLib = mpMgr->GetLib( maLibName ); + SbModule* pMod = pLib ? pLib->FindModule( aName ) : nullptr; + if( pMod ) + { + pLib->Remove( pMod ); + pLib->SetModified( false ); + } + } +} + +BasicError::BasicError( ErrCode nId, BasicErrorReason nR ) +{ + nErrorId = nId; + nReason = nR; +} + +BasicError::BasicError( const BasicError& rErr ) +{ + nErrorId = rErr.nErrorId; + nReason = rErr.nReason; +} + + +class BasicLibInfo +{ +private: + StarBASICRef xLib; + OUString aLibName; + OUString aStorageName; // string is sufficient, unique at runtime + OUString aRelStorageName; + OUString aPassword; + + bool bDoLoad; + bool bReference; + + // Lib represents library in new UNO library container + uno::Reference< script::XLibraryContainer > mxScriptCont; + +public: + BasicLibInfo(); + + bool IsReference() const { return bReference; } + void SetReference(bool b) { bReference = b; } + + bool IsExtern() const { return aStorageName != szImbedded; } + + void SetStorageName( const OUString& rName ) { aStorageName = rName; } + const OUString& GetStorageName() const { return aStorageName; } + + void SetRelStorageName( const OUString& rN ) { aRelStorageName = rN; } + const OUString& GetRelStorageName() const { return aRelStorageName; } + + StarBASICRef GetLib() const + { + if( mxScriptCont.is() && mxScriptCont->hasByName( aLibName ) && + !mxScriptCont->isLibraryLoaded( aLibName ) ) + return StarBASICRef(); + return xLib; + } + StarBASICRef& GetLibRef() { return xLib; } + void SetLib( StarBASIC* pBasic ) { xLib = pBasic; } + + const OUString& GetLibName() const { return aLibName; } + void SetLibName( const OUString& rName ) { aLibName = rName; } + + // Only temporary for Load/Save + bool DoLoad() const { return bDoLoad; } + + bool HasPassword() const { return !aPassword.isEmpty(); } + const OUString& GetPassword() const { return aPassword; } + void SetPassword( const OUString& rNewPassword ) + { aPassword = rNewPassword; } + + static BasicLibInfo* Create( SotStorageStream& rSStream ); + + const uno::Reference< script::XLibraryContainer >& GetLibraryContainer() const + { return mxScriptCont; } + void SetLibraryContainer( const uno::Reference< script::XLibraryContainer >& xScriptCont ) + { mxScriptCont = xScriptCont; } +}; + + +BasicLibInfo::BasicLibInfo() + : aStorageName(szImbedded) + , aRelStorageName(szImbedded) + , bDoLoad(false) + , bReference(false) +{ +} + +BasicLibInfo* BasicLibInfo::Create( SotStorageStream& rSStream ) +{ + BasicLibInfo* pInfo = new BasicLibInfo; + + sal_uInt32 nEndPos; + sal_uInt16 nId; + sal_uInt16 nVer; + + rSStream.ReadUInt32( nEndPos ); + rSStream.ReadUInt16( nId ); + rSStream.ReadUInt16( nVer ); + + DBG_ASSERT( nId == LIBINFO_ID, "No BasicLibInfo?!" ); + if( nId == LIBINFO_ID ) + { + // Reload? + bool bDoLoad; + rSStream.ReadCharAsBool( bDoLoad ); + pInfo->bDoLoad = bDoLoad; + + // The name of the lib... + OUString aName = rSStream.ReadUniOrByteString(rSStream.GetStreamCharSet()); + pInfo->SetLibName( aName ); + + // Absolute path... + OUString aStorageName = rSStream.ReadUniOrByteString(rSStream.GetStreamCharSet()); + pInfo->SetStorageName( aStorageName ); + + // Relative path... + OUString aRelStorageName = rSStream.ReadUniOrByteString(rSStream.GetStreamCharSet()); + pInfo->SetRelStorageName( aRelStorageName ); + + if ( nVer >= 2 ) + { + bool bReferenz; + rSStream.ReadCharAsBool( bReferenz ); + pInfo->SetReference(bReferenz); + } + + rSStream.Seek( nEndPos ); + } + return pInfo; +} + +BasicManager::BasicManager( SotStorage& rStorage, std::u16string_view rBaseURL, StarBASIC* pParentFromStdLib, OUString const * pLibPath, bool bDocMgr ) : mbDocMgr( bDocMgr ) +{ + if( pLibPath ) + { + aBasicLibPath = *pLibPath; + } + OUString aStorName( rStorage.GetName() ); + maStorageName = INetURLObject(aStorName, INetProtocol::File).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + + // If there is no Manager Stream, no further actions are necessary + if ( rStorage.IsStream( szManagerStream ) ) + { + LoadBasicManager( rStorage, rBaseURL ); + // StdLib contains Parent: + StarBASIC* pStdLib = GetStdLib(); + DBG_ASSERT( pStdLib, "Standard-Lib not loaded?" ); + if ( !pStdLib ) + { + // Should never happen, but if it happens we won't crash... + pStdLib = new StarBASIC( nullptr, mbDocMgr ); + + if (maLibs.empty()) + CreateLibInfo(); + + BasicLibInfo& rStdLibInfo = *maLibs.front(); + + rStdLibInfo.SetLib( pStdLib ); + StarBASICRef xStdLib = rStdLibInfo.GetLib(); + xStdLib->SetName( szStdLibName ); + rStdLibInfo.SetLibName( szStdLibName ); + xStdLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); + xStdLib->SetModified( false ); + } + else + { + pStdLib->SetParent( pParentFromStdLib ); + // The other get StdLib as parent: + + for ( sal_uInt16 nBasic = 1; nBasic < GetLibCount(); nBasic++ ) + { + StarBASIC* pBasic = GetLib( nBasic ); + if ( pBasic ) + { + pStdLib->Insert( pBasic ); + pBasic->SetFlag( SbxFlagBits::ExtSearch ); + } + } + // Modified through insert + pStdLib->SetModified( false ); + } + } + else + { + ImpCreateStdLib( pParentFromStdLib ); + if ( rStorage.IsStream( szOldManagerStream ) ) + LoadOldBasicManager( rStorage ); + } +} + +static void copyToLibraryContainer( StarBASIC* pBasic, const LibraryContainerInfo& rInfo ) +{ + uno::Reference< script::XLibraryContainer > xScriptCont( rInfo.mxScriptCont ); + if ( !xScriptCont.is() ) + return; + + OUString aLibName = pBasic->GetName(); + if( !xScriptCont->hasByName( aLibName ) ) + xScriptCont->createLibrary( aLibName ); + + uno::Any aLibAny = xScriptCont->getByName( aLibName ); + uno::Reference< container::XNameContainer > xLib; + aLibAny >>= xLib; + if ( !xLib.is() ) + return; + + for ( const auto& pModule: pBasic->GetModules() ) + { + OUString aModName = pModule->GetName(); + if( !xLib->hasByName( aModName ) ) + { + OUString aSource = pModule->GetSource32(); + uno::Any aSourceAny; + aSourceAny <<= aSource; + xLib->insertByName( aModName, aSourceAny ); + } + } +} + +const uno::Reference< script::XPersistentLibraryContainer >& BasicManager::GetDialogLibraryContainer() const +{ + return maContainerInfo.mxDialogCont; +} + +const uno::Reference< script::XPersistentLibraryContainer >& BasicManager::GetScriptLibraryContainer() const +{ + return maContainerInfo.mxScriptCont; +} + +void BasicManager::SetLibraryContainerInfo( const LibraryContainerInfo& rInfo ) +{ + maContainerInfo = rInfo; + + uno::Reference< script::XLibraryContainer > xScriptCont( maContainerInfo.mxScriptCont ); + if( xScriptCont.is() ) + { + // Register listener for lib container + uno::Reference< container::XContainerListener > xLibContainerListener + = new BasMgrContainerListenerImpl( this, "" ); + + uno::Reference< container::XContainer> xLibContainer( xScriptCont, uno::UNO_QUERY ); + xLibContainer->addContainerListener( xLibContainerListener ); + + const uno::Sequence< OUString > aScriptLibNames = xScriptCont->getElementNames(); + + if( aScriptLibNames.hasElements() ) + { + for(const auto& rScriptLibName : aScriptLibNames) + { + uno::Any aLibAny = xScriptCont->getByName( rScriptLibName ); + + if ( rScriptLibName == "Standard" || rScriptLibName == "VBAProject") + xScriptCont->loadLibrary( rScriptLibName ); + + BasMgrContainerListenerImpl::insertLibraryImpl + ( xScriptCont, this, aLibAny, rScriptLibName ); + } + } + else + { + // No libs? Maybe an 5.2 document already loaded + for (auto const& rpBasLibInfo : maLibs) + { + StarBASIC* pLib = rpBasLibInfo->GetLib().get(); + if( !pLib ) + { + bool bLoaded = ImpLoadLibrary( rpBasLibInfo.get(), nullptr ); + if( bLoaded ) + pLib = rpBasLibInfo->GetLib().get(); + } + if( pLib ) + { + copyToLibraryContainer( pLib, maContainerInfo ); + if (rpBasLibInfo->HasPassword()) + { + OldBasicPassword* pOldBasicPassword = + maContainerInfo.mpOldBasicPassword; + if( pOldBasicPassword ) + { + pOldBasicPassword->setLibraryPassword( + pLib->GetName(), rpBasLibInfo->GetPassword() ); + } + } + } + } + } + } + + SetGlobalUNOConstant( "BasicLibraries", uno::Any( maContainerInfo.mxScriptCont ) ); + SetGlobalUNOConstant( "DialogLibraries", uno::Any( maContainerInfo.mxDialogCont ) ); +} + +BasicManager::BasicManager( StarBASIC* pSLib, OUString const * pLibPath, bool bDocMgr ) : mbDocMgr( bDocMgr ) +{ + DBG_ASSERT( pSLib, "BasicManager cannot be created with a NULL-Pointer!" ); + + if( pLibPath ) + { + aBasicLibPath = *pLibPath; + } + BasicLibInfo* pStdLibInfo = CreateLibInfo(); + pStdLibInfo->SetLib( pSLib ); + StarBASICRef xStdLib = pStdLibInfo->GetLib(); + xStdLib->SetName(szStdLibName); + pStdLibInfo->SetLibName(szStdLibName ); + pSLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); + + // Save is only necessary if basic has changed + xStdLib->SetModified( false ); +} + +void BasicManager::ImpMgrNotLoaded( const OUString& rStorageName ) +{ + // pErrInf is only destroyed if the error os processed by an + // ErrorHandler + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_MGROPEN, rStorageName, DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::OPENMGRSTREAM); + + // Create a stdlib otherwise we crash! + BasicLibInfo* pStdLibInfo = CreateLibInfo(); + pStdLibInfo->SetLib( new StarBASIC( nullptr, mbDocMgr ) ); + StarBASICRef xStdLib = pStdLibInfo->GetLib(); + xStdLib->SetName( szStdLibName ); + pStdLibInfo->SetLibName( szStdLibName ); + xStdLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); + xStdLib->SetModified( false ); +} + + +void BasicManager::ImpCreateStdLib( StarBASIC* pParentFromStdLib ) +{ + BasicLibInfo* pStdLibInfo = CreateLibInfo(); + StarBASIC* pStdLib = new StarBASIC( pParentFromStdLib, mbDocMgr ); + pStdLibInfo->SetLib( pStdLib ); + pStdLib->SetName( szStdLibName ); + pStdLibInfo->SetLibName( szStdLibName ); + pStdLib->SetFlag( SbxFlagBits::DontStore | SbxFlagBits::ExtSearch ); +} + +void BasicManager::LoadBasicManager( SotStorage& rStorage, std::u16string_view rBaseURL ) +{ + tools::SvRef<SotStorageStream> xManagerStream = rStorage.OpenSotStream( szManagerStream, eStreamReadMode ); + + OUString aStorName( rStorage.GetName() ); + // #i13114 removed, DBG_ASSERT( aStorName.Len(), "No Storage Name!" ); + + if ( !xManagerStream.is() || xManagerStream->GetError() || ( xManagerStream->TellEnd() == 0 ) ) + { + ImpMgrNotLoaded( aStorName ); + return; + } + + maStorageName = INetURLObject(aStorName, INetProtocol::File).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + // #i13114 removed, DBG_ASSERT(aStorageName.Len() != 0, "Bad storage name"); + + OUString aRealStorageName = maStorageName; // for relative paths, can be modified through BaseURL + + if ( !rBaseURL.empty() ) + { + INetURLObject aObj( rBaseURL ); + if ( aObj.GetProtocol() == INetProtocol::File ) + { + aRealStorageName = aObj.PathToFileName(); + } + } + + xManagerStream->SetBufferSize( 1024 ); + xManagerStream->Seek( STREAM_SEEK_TO_BEGIN ); + + sal_uInt32 nEndPos; + xManagerStream->ReadUInt32( nEndPos ); + + sal_uInt16 nLibs; + xManagerStream->ReadUInt16( nLibs ); + // Plausibility! + if( nLibs & 0xF000 ) + { + SAL_WARN( "basic", "BasicManager-Stream defect!" ); + return; + } + const size_t nMinBasicLibSize(8); + const size_t nMaxPossibleLibs = xManagerStream->remainingSize() / nMinBasicLibSize; + if (nLibs > nMaxPossibleLibs) + { + SAL_WARN("basic", "Parsing error: " << nMaxPossibleLibs << + " max possible entries, but " << nLibs << " claimed, truncating"); + nLibs = nMaxPossibleLibs; + } + for (sal_uInt16 nL = 0; nL < nLibs; ++nL) + { + BasicLibInfo* pInfo = BasicLibInfo::Create( *xManagerStream ); + + // Correct absolute pathname if relative is existing. + // Always try relative first if there are two stands on disk + if ( !pInfo->GetRelStorageName().isEmpty() && pInfo->GetRelStorageName() != szImbedded ) + { + INetURLObject aObj( aRealStorageName, INetProtocol::File ); + aObj.removeSegment(); + bool bWasAbsolute = false; + aObj = aObj.smartRel2Abs( pInfo->GetRelStorageName(), bWasAbsolute ); + + //*** TODO: Replace if still necessary + //*** TODO-End + if ( ! aBasicLibPath.isEmpty() ) + { + // Search lib in path + OUString aSearchFile = pInfo->GetRelStorageName(); + OUString aSearchFileOldFormat(aSearchFile); + SvtPathOptions aPathCFG; + if( aPathCFG.SearchFile( aSearchFileOldFormat, SvtPathOptions::Paths::Basic ) ) + { + pInfo->SetStorageName( aSearchFile ); + } + } + } + + maLibs.push_back(std::unique_ptr<BasicLibInfo>(pInfo)); + // Libs from external files should be loaded only when necessary. + // But references are loaded at once, otherwise some big customers get into trouble + if ( pInfo->DoLoad() && + ( !pInfo->IsExtern() || pInfo->IsReference())) + { + ImpLoadLibrary( pInfo, &rStorage ); + } + } + + xManagerStream->Seek( nEndPos ); + xManagerStream->SetBufferSize( 0 ); + xManagerStream.clear(); +} + +void BasicManager::LoadOldBasicManager( SotStorage& rStorage ) +{ + tools::SvRef<SotStorageStream> xManagerStream = rStorage.OpenSotStream( szOldManagerStream, eStreamReadMode ); + + OUString aStorName( rStorage.GetName() ); + DBG_ASSERT( aStorName.getLength(), "No Storage Name!" ); + + if ( !xManagerStream.is() || xManagerStream->GetError() || ( xManagerStream->TellEnd() == 0 ) ) + { + ImpMgrNotLoaded( aStorName ); + return; + } + + xManagerStream->SetBufferSize( 1024 ); + xManagerStream->Seek( STREAM_SEEK_TO_BEGIN ); + sal_uInt32 nBasicStartOff, nBasicEndOff; + xManagerStream->ReadUInt32( nBasicStartOff ); + xManagerStream->ReadUInt32( nBasicEndOff ); + + DBG_ASSERT( !xManagerStream->GetError(), "Invalid Manager-Stream!" ); + + xManagerStream->Seek( nBasicStartOff ); + if (!ImplLoadBasic( *xManagerStream, maLibs.front()->GetLibRef() )) + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_MGROPEN, aStorName, DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::OPENMGRSTREAM); + // and it proceeds ... + } + xManagerStream->Seek( nBasicEndOff+1 ); // +1: 0x00 as separator + OUString aLibs = xManagerStream->ReadUniOrByteString(xManagerStream->GetStreamCharSet()); + xManagerStream->SetBufferSize( 0 ); + xManagerStream.clear(); // Close stream + + if ( aLibs.isEmpty() ) + return; + + INetURLObject aCurStorage( aStorName, INetProtocol::File ); + sal_Int32 nLibPos {0}; + do { + const OUString aLibInfo(aLibs.getToken(0, LIB_SEP, nLibPos)); + sal_Int32 nInfoPos {0}; + const OUString aLibName( aLibInfo.getToken( 0, LIBINFO_SEP, nInfoPos ) ); + DBG_ASSERT( nInfoPos >= 0, "Invalid Lib-Info!" ); + const OUString aLibAbsStorageName( aLibInfo.getToken( 0, LIBINFO_SEP, nInfoPos ) ); + // TODO: fail also here if there are no more tokens? + const OUString aLibRelStorageName( aLibInfo.getToken( 0, LIBINFO_SEP, nInfoPos ) ); + DBG_ASSERT( nInfoPos < 0, "Invalid Lib-Info!" ); + INetURLObject aLibAbsStorage( aLibAbsStorageName, INetProtocol::File ); + + INetURLObject aLibRelStorage( aStorName ); + aLibRelStorage.removeSegment(); + bool bWasAbsolute = false; + aLibRelStorage = aLibRelStorage.smartRel2Abs( aLibRelStorageName, bWasAbsolute); + DBG_ASSERT(!bWasAbsolute, "RelStorageName was absolute!" ); + + tools::SvRef<SotStorage> xStorageRef; + if ( aLibAbsStorage == aCurStorage || aLibRelStorageName == szImbedded ) + { + xStorageRef = &rStorage; + } + else + { + xStorageRef = new SotStorage( false, aLibAbsStorage.GetMainURL + ( INetURLObject::DecodeMechanism::NONE ), eStorageReadMode ); + if ( xStorageRef->GetError() != ERRCODE_NONE ) + xStorageRef = new SotStorage( false, aLibRelStorage. + GetMainURL( INetURLObject::DecodeMechanism::NONE ), eStorageReadMode ); + } + if ( xStorageRef.is() ) + { + AddLib( *xStorageRef, aLibName, false ); + } + else + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_LIBLOAD, aStorName, DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::STORAGENOTFOUND); + } + } while (nLibPos>=0); +} + +BasicManager::~BasicManager() +{ + // Notify listener if something needs to be saved + Broadcast( SfxHint( SfxHintId::Dying) ); +} + +bool BasicManager::HasExeCode( std::u16string_view sLib ) +{ + StarBASIC* pLib = GetLib(sLib); + if ( pLib ) + { + for (const auto& pModule: pLib->GetModules()) + { + if (pModule->HasExeCode()) + return true; + } + } + return false; +} + +BasicLibInfo* BasicManager::CreateLibInfo() +{ + maLibs.push_back(std::make_unique<BasicLibInfo>()); + return maLibs.back().get(); +} + +bool BasicManager::ImpLoadLibrary( BasicLibInfo* pLibInfo, SotStorage* pCurStorage ) +{ + try { + DBG_ASSERT( pLibInfo, "LibInfo!?" ); + + OUString aStorageName( pLibInfo->GetStorageName() ); + if ( aStorageName.isEmpty() || aStorageName == szImbedded ) + { + aStorageName = GetStorageName(); + } + tools::SvRef<SotStorage> xStorage; + // The current must not be opened again... + if ( pCurStorage ) + { + OUString aStorName( pCurStorage->GetName() ); + // #i13114 removed, DBG_ASSERT( aStorName.Len(), "No Storage Name!" ); + + INetURLObject aCurStorageEntry(aStorName, INetProtocol::File); + // #i13114 removed, DBG_ASSERT(aCurStorageEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE ).Len() != 0, "Bad storage name"); + + INetURLObject aStorageEntry(aStorageName, INetProtocol::File); + // #i13114 removed, DBG_ASSERT(aCurStorageEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE ).Len() != 0, "Bad storage name"); + + if ( aCurStorageEntry == aStorageEntry ) + { + xStorage = pCurStorage; + } + } + + if ( !xStorage.is() ) + { + xStorage = new SotStorage( false, aStorageName, eStorageReadMode ); + } + tools::SvRef<SotStorage> xBasicStorage = xStorage->OpenSotStorage( szBasicStorage, eStorageReadMode, false ); + + if ( !xBasicStorage.is() || xBasicStorage->GetError() ) + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_MGROPEN, xStorage->GetName(), DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::OPENLIBSTORAGE); + } + else + { + // In the Basic-Storage every lib is in a Stream... + tools::SvRef<SotStorageStream> xBasicStream = xBasicStorage->OpenSotStream( pLibInfo->GetLibName(), eStreamReadMode ); + if ( !xBasicStream.is() || xBasicStream->GetError() ) + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_LIBLOAD , pLibInfo->GetLibName(), DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::OPENLIBSTREAM); + } + else + { + bool bLoaded = false; + if ( xBasicStream->TellEnd() != 0 ) + { + if ( !pLibInfo->GetLib().is() ) + { + pLibInfo->SetLib( new StarBASIC( GetStdLib(), mbDocMgr ) ); + } + xBasicStream->SetBufferSize( 1024 ); + xBasicStream->Seek( STREAM_SEEK_TO_BEGIN ); + bLoaded = ImplLoadBasic( *xBasicStream, pLibInfo->GetLibRef() ); + xBasicStream->SetBufferSize( 0 ); + StarBASICRef xStdLib = pLibInfo->GetLib(); + xStdLib->SetName( pLibInfo->GetLibName() ); + xStdLib->SetModified( false ); + xStdLib->SetFlag( SbxFlagBits::DontStore ); + } + if ( !bLoaded ) + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_LIBLOAD, pLibInfo->GetLibName(), DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::BASICLOADERROR); + } + else + { + // Perhaps there are additional information in the stream... + xBasicStream->SetCryptMaskKey(szCryptingKey); + xBasicStream->RefreshBuffer(); + sal_uInt32 nPasswordMarker = 0; + xBasicStream->ReadUInt32( nPasswordMarker ); + if ( ( nPasswordMarker == PASSWORD_MARKER ) && !xBasicStream->eof() ) + { + OUString aPassword = xBasicStream->ReadUniOrByteString( + xBasicStream->GetStreamCharSet()); + pLibInfo->SetPassword( aPassword ); + } + xBasicStream->SetCryptMaskKey(OString()); + CheckModules( pLibInfo->GetLib().get(), pLibInfo->IsReference() ); + } + return bLoaded; + } + } + } + catch (const css::ucb::ContentCreationException&) + { + } + return false; +} + +bool BasicManager::ImplEncryptStream( SvStream& rStrm ) +{ + sal_uInt64 const nPos = rStrm.Tell(); + sal_uInt32 nCreator; + rStrm.ReadUInt32( nCreator ); + rStrm.Seek( nPos ); + bool bProtected = false; + if ( nCreator != SBXCR_SBX ) + { + // Should only be the case for encrypted Streams + bProtected = true; + rStrm.SetCryptMaskKey(szCryptingKey); + rStrm.RefreshBuffer(); + } + return bProtected; +} + +// This code is necessary to load the BASIC of Beta 1 +// TODO: Which Beta 1? +bool BasicManager::ImplLoadBasic( SvStream& rStrm, StarBASICRef& rOldBasic ) const +{ + bool bProtected = ImplEncryptStream( rStrm ); + SbxBaseRef xNew = SbxBase::Load( rStrm ); + bool bLoaded = false; + if( xNew.is() ) + { + if( auto pNew = dynamic_cast<StarBASIC*>( xNew.get() ) ) + { + // Use the Parent of the old BASICs + if( rOldBasic.is() ) + { + pNew->SetParent( rOldBasic->GetParent() ); + if( pNew->GetParent() ) + { + pNew->GetParent()->Insert( pNew ); + } + pNew->SetFlag( SbxFlagBits::ExtSearch ); + } + rOldBasic = pNew; + + // Fill new library container (5.2 -> 6.0) + copyToLibraryContainer( pNew, maContainerInfo ); + + pNew->SetModified( false ); + bLoaded = true; + } + } + if ( bProtected ) + { + rStrm.SetCryptMaskKey(OString()); + } + return bLoaded; +} + +void BasicManager::CheckModules( StarBASIC* pLib, bool bReference ) +{ + if ( !pLib ) + { + return; + } + bool bModified = pLib->IsModified(); + + for ( const auto& pModule: pLib->GetModules() ) + { + DBG_ASSERT(pModule, "Module not received!"); + if ( !pModule->IsCompiled() && !StarBASIC::GetErrorCode() ) + { + pModule->Compile(); + } + } + + // #67477, AB 8.12.99 On demand compile in referenced libs should not + // cause modified + if( !bModified && bReference ) + { + OSL_FAIL( "Referenced basic library is not compiled!" ); + pLib->SetModified( false ); + } +} + +StarBASIC* BasicManager::AddLib( SotStorage& rStorage, const OUString& rLibName, bool bReference ) +{ + OUString aStorName( rStorage.GetName() ); + DBG_ASSERT( !aStorName.isEmpty(), "No Storage Name!" ); + + OUString aStorageName = INetURLObject(aStorName, INetProtocol::File).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + DBG_ASSERT(!aStorageName.isEmpty(), "Bad storage name"); + + OUString aNewLibName( rLibName ); + while ( HasLib( aNewLibName ) ) + { + aNewLibName += "_"; + } + BasicLibInfo* pLibInfo = CreateLibInfo(); + // Use original name otherwise ImpLoadLibrary fails... + pLibInfo->SetLibName( rLibName ); + // but doesn't work this way if name exists twice + sal_uInt16 nLibId = static_cast<sal_uInt16>(maLibs.size()) - 1; + + // Set StorageName before load because it is compared with pCurStorage + pLibInfo->SetStorageName( aStorageName ); + bool bLoaded = ImpLoadLibrary( pLibInfo, &rStorage ); + + if ( bLoaded ) + { + if ( aNewLibName != rLibName ) + { + pLibInfo->SetLibName(aNewLibName); + } + if ( bReference ) + { + pLibInfo->GetLib()->SetModified( false ); // Don't save in this case + pLibInfo->SetRelStorageName( OUString() ); + pLibInfo->SetReference(true); + } + else + { + pLibInfo->GetLib()->SetModified( true ); // Must be saved after Add! + pLibInfo->SetStorageName( szImbedded ); // Save in BasicManager-Storage + } + } + else + { + RemoveLib( nLibId, false ); + pLibInfo = nullptr; + } + + return pLibInfo ? &*pLibInfo->GetLib() : nullptr; + +} + +bool BasicManager::IsReference( sal_uInt16 nLib ) +{ + DBG_ASSERT( nLib < maLibs.size(), "Lib does not exist!" ); + if ( nLib < maLibs.size() ) + { + return maLibs[nLib]->IsReference(); + } + return false; +} + +void BasicManager::RemoveLib( sal_uInt16 nLib ) +{ + // Only physical deletion if no reference + RemoveLib( nLib, !IsReference( nLib ) ); +} + +bool BasicManager::RemoveLib( sal_uInt16 nLib, bool bDelBasicFromStorage ) +{ + DBG_ASSERT( nLib, "Standard-Lib cannot be removed!" ); + + DBG_ASSERT( !nLib || nLib < maLibs.size(), "Lib not found!" ); + + if( !nLib || nLib < maLibs.size() ) + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_REMOVELIB, OUString(), DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::STDLIB); + return false; + } + + auto const itLibInfo = maLibs.begin() + nLib; + + // If one of the streams cannot be opened, this is not an error, + // because BASIC was never written before... + if (bDelBasicFromStorage && !(*itLibInfo)->IsReference() && + (!(*itLibInfo)->IsExtern() || SotStorage::IsStorageFile((*itLibInfo)->GetStorageName()))) + { + tools::SvRef<SotStorage> xStorage; + try + { + if (!(*itLibInfo)->IsExtern()) + { + xStorage = new SotStorage(false, GetStorageName()); + } + else + { + xStorage = new SotStorage(false, (*itLibInfo)->GetStorageName()); + } + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION("basic", "BasicManager::RemoveLib:"); + } + + if (xStorage.is() && xStorage->IsStorage(szBasicStorage)) + { + tools::SvRef<SotStorage> xBasicStorage = xStorage->OpenSotStorage + ( szBasicStorage, StreamMode::STD_READWRITE, false ); + + if ( !xBasicStorage.is() || xBasicStorage->GetError() ) + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_REMOVELIB, OUString(), DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::OPENLIBSTORAGE); + } + else if (xBasicStorage->IsStream((*itLibInfo)->GetLibName())) + { + xBasicStorage->Remove((*itLibInfo)->GetLibName()); + xBasicStorage->Commit(); + + // If no further stream available, + // delete the SubStorage. + SvStorageInfoList aInfoList; + xBasicStorage->FillInfoList( &aInfoList ); + if ( aInfoList.empty() ) + { + xBasicStorage.clear(); + xStorage->Remove( szBasicStorage ); + xStorage->Commit(); + // If no further Streams or SubStorages available, + // delete the Storage, too. + aInfoList.clear(); + xStorage->FillInfoList( &aInfoList ); + if ( aInfoList.empty() ) + { + //OUString aName_( xStorage->GetName() ); + xStorage.clear(); + //*** TODO: Replace if still necessary + //SfxContentHelper::Kill( aName ); + //*** TODO-End + } + } + } + } + } + if ((*itLibInfo)->GetLib().is()) + { + GetStdLib()->Remove( (*itLibInfo)->GetLib().get() ); + } + maLibs.erase(itLibInfo); + return true; // Remove was successful, del unimportant +} + +sal_uInt16 BasicManager::GetLibCount() const +{ + return static_cast<sal_uInt16>(maLibs.size()); +} + +StarBASIC* BasicManager::GetLib( sal_uInt16 nLib ) const +{ + DBG_ASSERT( nLib < maLibs.size(), "Lib does not exist!" ); + if ( nLib < maLibs.size() ) + { + return maLibs[nLib]->GetLib().get(); + } + return nullptr; +} + +StarBASIC* BasicManager::GetStdLib() const +{ + StarBASIC* pLib = GetLib( 0 ); + return pLib; +} + +StarBASIC* BasicManager::GetLib( std::u16string_view rName ) const +{ + for (auto const& rpLib : maLibs) + { + if (rpLib->GetLibName().equalsIgnoreAsciiCase(rName)) // Check if available... + { + return rpLib->GetLib().get(); + } + } + return nullptr; +} + +sal_uInt16 BasicManager::GetLibId( std::u16string_view rName ) const +{ + for (size_t i = 0; i < maLibs.size(); i++) + { + if (maLibs[i]->GetLibName().equalsIgnoreAsciiCase( rName )) + { + return static_cast<sal_uInt16>(i); + } + } + return LIB_NOTFOUND; +} + +bool BasicManager::HasLib( std::u16string_view rName ) const +{ + for (const auto& rpLib : maLibs) + { + if (rpLib->GetLibName().equalsIgnoreAsciiCase(rName)) // Check if available... + { + return true; + } + } + return false; +} + +OUString BasicManager::GetLibName( sal_uInt16 nLib ) +{ + DBG_ASSERT( nLib < maLibs.size(), "Lib?!" ); + if ( nLib < maLibs.size() ) + { + return maLibs[nLib]->GetLibName(); + } + return OUString(); +} + +bool BasicManager::LoadLib( sal_uInt16 nLib ) +{ + bool bDone = false; + DBG_ASSERT( nLib < maLibs.size() , "Lib?!" ); + if ( nLib < maLibs.size() ) + { + BasicLibInfo& rLibInfo = *maLibs[nLib]; + uno::Reference< script::XLibraryContainer > xLibContainer = rLibInfo.GetLibraryContainer(); + if( xLibContainer.is() ) + { + OUString aLibName = rLibInfo.GetLibName(); + xLibContainer->loadLibrary( aLibName ); + bDone = xLibContainer->isLibraryLoaded( aLibName ); + } + else + { + bDone = ImpLoadLibrary( &rLibInfo, nullptr ); + StarBASIC* pLib = GetLib( nLib ); + if ( pLib ) + { + GetStdLib()->Insert( pLib ); + pLib->SetFlag( SbxFlagBits::ExtSearch ); + } + } + } + else + { + StringErrorInfo* pErrInf = new StringErrorInfo( ERRCODE_BASMGR_LIBLOAD, OUString(), DialogMask::ButtonsOk ); + aErrors.emplace_back(*pErrInf, BasicErrorReason::LIBNOTFOUND); + } + return bDone; +} + +StarBASIC* BasicManager::CreateLib( const OUString& rLibName ) +{ + if ( GetLib( rLibName ) ) + { + return nullptr; + } + BasicLibInfo* pLibInfo = CreateLibInfo(); + StarBASIC* pNew = new StarBASIC( GetStdLib(), mbDocMgr ); + GetStdLib()->Insert( pNew ); + pNew->SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::DontStore ); + pLibInfo->SetLib( pNew ); + pLibInfo->SetLibName( rLibName ); + pLibInfo->GetLib()->SetName( rLibName ); + return pLibInfo->GetLib().get(); +} + +// For XML import/export: +StarBASIC* BasicManager::CreateLib( const OUString& rLibName, const OUString& Password, + const OUString& LinkTargetURL ) +{ + // Ask if lib exists because standard lib is always there + StarBASIC* pLib = GetLib( rLibName ); + if( !pLib ) + { + if( !LinkTargetURL.isEmpty()) + { + try + { + tools::SvRef<SotStorage> xStorage = new SotStorage(false, LinkTargetURL, StreamMode::READ | StreamMode::SHARE_DENYWRITE); + if (!xStorage->GetError()) + { + pLib = AddLib(*xStorage, rLibName, true); + } + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION("basic", "BasicManager::RemoveLib:"); + } + DBG_ASSERT( pLib, "XML Import: Linked basic library could not be loaded"); + } + else + { + pLib = CreateLib( rLibName ); + if( Password.isEmpty()) + { + BasicLibInfo* pLibInfo = FindLibInfo( pLib ); + pLibInfo ->SetPassword( Password ); + } + } + //ExternalSourceURL ? + } + return pLib; +} + +StarBASIC* BasicManager::CreateLibForLibContainer( const OUString& rLibName, + const uno::Reference< script::XLibraryContainer >& xScriptCont ) +{ + if ( GetLib( rLibName ) ) + { + return nullptr; + } + BasicLibInfo* pLibInfo = CreateLibInfo(); + StarBASIC* pNew = new StarBASIC( GetStdLib(), mbDocMgr ); + GetStdLib()->Insert( pNew ); + pNew->SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::DontStore ); + pLibInfo->SetLib( pNew ); + pLibInfo->SetLibName( rLibName ); + pLibInfo->GetLib()->SetName( rLibName ); + pLibInfo->SetLibraryContainer( xScriptCont ); + return pNew; +} + + +BasicLibInfo* BasicManager::FindLibInfo( StarBASIC const * pBasic ) +{ + for (auto const& rpLib : maLibs) + { + if (rpLib->GetLib().get() == pBasic) + { + return rpLib.get(); + } + } + return nullptr; +} + + +bool BasicManager::IsBasicModified() const +{ + for (auto const& rpLib : maLibs) + { + if (rpLib->GetLib().is() && rpLib->GetLib()->IsModified()) + { + return true; + } + } + return false; +} + + +bool BasicManager::GetGlobalUNOConstant( const OUString& rName, uno::Any& aOut ) +{ + bool bRes = false; + StarBASIC* pStandardLib = GetStdLib(); + OSL_PRECOND( pStandardLib, "BasicManager::GetGlobalUNOConstant: no lib to read from!" ); + if ( pStandardLib ) + bRes = pStandardLib->GetUNOConstant( rName, aOut ); + return bRes; +} + +void BasicManager::SetGlobalUNOConstant( const OUString& rName, const uno::Any& _rValue, css::uno::Any* pOldValue ) +{ + StarBASIC* pStandardLib = GetStdLib(); + OSL_PRECOND( pStandardLib, "BasicManager::SetGlobalUNOConstant: no lib to insert into!" ); + if ( !pStandardLib ) + return; + + if (pOldValue) + { + // obtain the old value + SbxVariable* pVariable = pStandardLib->Find( rName, SbxClassType::Object ); + if ( pVariable ) + *pOldValue = sbxToUnoValue( pVariable ); + } + SbxObjectRef xUnoObj = GetSbUnoObject( _rValue.getValueType ().getTypeName () , _rValue ); + xUnoObj->SetName(rName); + xUnoObj->SetFlag( SbxFlagBits::DontStore ); + pStandardLib->Insert( xUnoObj.get() ); +} + +bool BasicManager::LegacyPsswdBinaryLimitExceeded( std::vector< OUString >& _out_rModuleNames ) +{ + try + { + uno::Reference< container::XNameAccess > xScripts( GetScriptLibraryContainer(), uno::UNO_QUERY_THROW ); + uno::Reference< script::XLibraryContainerPassword > xPassword( GetScriptLibraryContainer(), uno::UNO_QUERY_THROW ); + + const uno::Sequence< OUString > aNames( xScripts->getElementNames() ); + for ( auto const & scriptElementName : aNames ) + { + if( !xPassword->isLibraryPasswordProtected( scriptElementName ) ) + continue; + + StarBASIC* pBasicLib = GetLib( scriptElementName ); + if ( !pBasicLib ) + continue; + + uno::Reference< container::XNameAccess > xScriptLibrary( xScripts->getByName( scriptElementName ), uno::UNO_QUERY_THROW ); + const uno::Sequence< OUString > aElementNames( xScriptLibrary->getElementNames() ); + sal_Int32 nLen = aElementNames.getLength(); + + std::vector< OUString > aBigModules( nLen ); + sal_Int32 nBigModules = 0; + + for ( auto const & libraryElementName : aElementNames ) + { + SbModule* pMod = pBasicLib->FindModule( libraryElementName ); + if ( pMod && pMod->ExceedsLegacyModuleSize() ) + aBigModules[ nBigModules++ ] = libraryElementName; + } + + if ( nBigModules ) + { + _out_rModuleNames.swap(aBigModules); + return true; + } + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basic"); + } + return false; +} + + +namespace +{ + SbMethod* lcl_queryMacro( BasicManager* i_manager, OUString const& i_fullyQualifiedName ) + { + sal_Int32 nLast = 0; + const OUString sLibName {i_fullyQualifiedName.getToken( 0, '.', nLast )}; + const OUString sModule {i_fullyQualifiedName.getToken( 0, '.', nLast )}; + OUString sMacro; + if(nLast >= 0) + { + sMacro = i_fullyQualifiedName.copy(nLast); + } + else + { + sMacro = i_fullyQualifiedName; + } + + utl::TransliterationWrapper& rTransliteration = SbGlobal::GetTransliteration(); + sal_uInt16 nLibCount = i_manager->GetLibCount(); + for ( sal_uInt16 nLib = 0; nLib < nLibCount; ++nLib ) + { + if ( rTransliteration.isEqual( i_manager->GetLibName( nLib ), sLibName ) ) + { + StarBASIC* pLib = i_manager->GetLib( nLib ); + if( !pLib ) + { + bool const bLoaded = i_manager->LoadLib( nLib ); + if (bLoaded) + { + pLib = i_manager->GetLib( nLib ); + } + } + + if( pLib ) + { + for ( const auto& pMod: pLib->GetModules() ) + { + if ( rTransliteration.isEqual( pMod->GetName(), sModule ) ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMod->Find( sMacro, SbxClassType::Method )); + if( pMethod ) + { + return pMethod; + } + } + } + } + } + } + return nullptr; + } +} + +bool BasicManager::HasMacro( OUString const& i_fullyQualifiedName ) const +{ + return ( lcl_queryMacro( const_cast< BasicManager* >( this ), i_fullyQualifiedName ) != nullptr ); +} + +ErrCode BasicManager::ExecuteMacro( OUString const& i_fullyQualifiedName, SbxArray* i_arguments, SbxValue* i_retValue ) +{ + SbMethod* pMethod = lcl_queryMacro( this, i_fullyQualifiedName ); + ErrCode nError = ERRCODE_NONE; + if ( pMethod ) + { + if ( i_arguments ) + pMethod->SetParameters( i_arguments ); + nError = pMethod->Call( i_retValue ); + } + else + nError = ERRCODE_BASIC_PROC_UNDEFINED; + return nError; +} + +ErrCode BasicManager::ExecuteMacro( OUString const& i_fullyQualifiedName, std::u16string_view i_commaSeparatedArgs, SbxValue* i_retValue ) +{ + SbMethod* pMethod = lcl_queryMacro( this, i_fullyQualifiedName ); + if ( !pMethod ) + { + return ERRCODE_BASIC_PROC_UNDEFINED; + } + // arguments must be quoted + OUString sQuotedArgs; + OUStringBuffer sArgs( i_commaSeparatedArgs ); + if ( sArgs.getLength()<2 || sArgs[1] == '\"') + { + // no args or already quoted args + sQuotedArgs = sArgs.makeStringAndClear(); + } + else + { + // quote parameters + sArgs.remove( 0, 1 ); + sArgs.remove( sArgs.getLength() - 1, 1 ); + + OUStringBuffer aBuff; + OUString sArgs2 = sArgs.makeStringAndClear(); + + aBuff.append("("); + if (!sArgs2.isEmpty()) + { + + sal_Int32 nPos {0}; + for (;;) + { + aBuff.append( "\"" ); + aBuff.append( o3tl::getToken(sArgs2, 0, ',', nPos) ); + aBuff.append( "\"" ); + if (nPos<0) + break; + aBuff.append( "," ); + } + } + aBuff.append( ")" ); + + sQuotedArgs = aBuff.makeStringAndClear(); + } + + // add quoted arguments and do the call + OUString sCall = "[" + + pMethod->GetName() + + sQuotedArgs + + "]"; + + SbxVariable* pRet = pMethod->GetParent()->Execute( sCall ); + if ( pRet && ( pRet != pMethod ) ) + { + *i_retValue = *pRet; + } + return SbxBase::GetError(); +} + +namespace { + +class ModuleInfo_Impl : public ModuleInfoHelper +{ + OUString maName; + OUString maLanguage; + OUString maSource; + +public: + ModuleInfo_Impl( OUString aName, OUString aLanguage, OUString aSource ) + : maName(std::move( aName )), maLanguage(std::move( aLanguage)), maSource(std::move( aSource )) {} + + // Methods XStarBasicModuleInfo + virtual OUString SAL_CALL getName() override + { return maName; } + virtual OUString SAL_CALL getLanguage() override + { return maLanguage; } + virtual OUString SAL_CALL getSource() override + { return maSource; } +}; + + +class DialogInfo_Impl : public WeakImplHelper< script::XStarBasicDialogInfo > +{ + OUString maName; + uno::Sequence< sal_Int8 > mData; + +public: + DialogInfo_Impl( OUString aName, const uno::Sequence< sal_Int8 >& Data ) + : maName(std::move( aName )), mData( Data ) {} + + // Methods XStarBasicDialogInfo + virtual OUString SAL_CALL getName() override + { return maName; } + virtual uno::Sequence< sal_Int8 > SAL_CALL getData() override + { return mData; } +}; + + +class LibraryInfo_Impl : public WeakImplHelper< script::XStarBasicLibraryInfo > +{ + OUString maName; + uno::Reference< container::XNameContainer > mxModuleContainer; + uno::Reference< container::XNameContainer > mxDialogContainer; + OUString maPassword; + OUString maExternaleSourceURL; + OUString maLinkTargetURL; + +public: + LibraryInfo_Impl + ( + OUString aName, + uno::Reference< container::XNameContainer > xModuleContainer, + uno::Reference< container::XNameContainer > xDialogContainer, + OUString aPassword, + OUString aExternaleSourceURL, + OUString aLinkTargetURL + ) + : maName(std::move( aName )) + , mxModuleContainer(std::move( xModuleContainer )) + , mxDialogContainer(std::move( xDialogContainer )) + , maPassword(std::move( aPassword )) + , maExternaleSourceURL(std::move( aExternaleSourceURL )) + , maLinkTargetURL(std::move( aLinkTargetURL )) + {} + + // Methods XStarBasicLibraryInfo + virtual OUString SAL_CALL getName() override + { return maName; } + virtual uno::Reference< container::XNameContainer > SAL_CALL getModuleContainer() override + { return mxModuleContainer; } + virtual uno::Reference< container::XNameContainer > SAL_CALL getDialogContainer() override + { return mxDialogContainer; } + virtual OUString SAL_CALL getPassword() override + { return maPassword; } + virtual OUString SAL_CALL getExternalSourceURL() override + { return maExternaleSourceURL; } + virtual OUString SAL_CALL getLinkTargetURL() override + { return maLinkTargetURL; } +}; + + +class ModuleContainer_Impl : public NameContainerHelper +{ + StarBASIC* mpLib; + +public: + explicit ModuleContainer_Impl( StarBASIC* pLib ) + :mpLib( pLib ) {} + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; +}; + +} + +// Methods XElementAccess +uno::Type ModuleContainer_Impl::getElementType() +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicModuleInfo>::get(); + return aModuleType; +} + +sal_Bool ModuleContainer_Impl::hasElements() +{ + return mpLib && !mpLib->GetModules().empty(); +} + +// Methods XNameAccess +uno::Any ModuleContainer_Impl::getByName( const OUString& aName ) +{ + SbModule* pMod = mpLib ? mpLib->FindModule( aName ) : nullptr; + if( !pMod ) + throw container::NoSuchElementException(); + uno::Reference< script::XStarBasicModuleInfo > xMod = new ModuleInfo_Impl( aName, "StarBasic", pMod->GetSource32() ); + uno::Any aRetAny; + aRetAny <<= xMod; + return aRetAny; +} + +uno::Sequence< OUString > ModuleContainer_Impl::getElementNames() +{ + sal_uInt16 nMods = mpLib ? mpLib->GetModules().size() : 0; + uno::Sequence< OUString > aRetSeq( nMods ); + OUString* pRetSeq = aRetSeq.getArray(); + for( sal_uInt16 i = 0 ; i < nMods ; i++ ) + { + pRetSeq[i] = mpLib->GetModules()[i]->GetName(); + } + return aRetSeq; +} + +sal_Bool ModuleContainer_Impl::hasByName( const OUString& aName ) +{ + SbModule* pMod = mpLib ? mpLib->FindModule( aName ) : nullptr; + bool bRet = (pMod != nullptr); + return bRet; +} + + +// Methods XNameReplace +void ModuleContainer_Impl::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + + +// Methods XNameContainer +void ModuleContainer_Impl::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicModuleInfo>::get(); + const uno::Type& aAnyType = aElement.getValueType(); + if( aModuleType != aAnyType ) + { + throw lang::IllegalArgumentException("types do not match", static_cast<cppu::OWeakObject*>(this), 2); + } + uno::Reference< script::XStarBasicModuleInfo > xMod; + aElement >>= xMod; + mpLib->MakeModule( aName, xMod->getSource() ); +} + +void ModuleContainer_Impl::removeByName( const OUString& Name ) +{ + SbModule* pMod = mpLib ? mpLib->FindModule( Name ) : nullptr; + if( !pMod ) + { + throw container::NoSuchElementException(); + } + mpLib->Remove( pMod ); +} + + +static uno::Sequence< sal_Int8 > implGetDialogData( SbxObject* pDialog ) +{ + SvMemoryStream aMemStream; + pDialog->Store( aMemStream ); + sal_Int32 nLen = aMemStream.Tell(); + if (nLen < 0) { abort(); } + uno::Sequence< sal_Int8 > aData( nLen ); + sal_Int8* pDestData = aData.getArray(); + const sal_Int8* pSrcData = static_cast<const sal_Int8*>(aMemStream.GetData()); + memcpy( pDestData, pSrcData, nLen ); + return aData; +} + +static SbxObjectRef implCreateDialog( const uno::Sequence< sal_Int8 >& aData ) +{ + sal_Int8* pData = const_cast< uno::Sequence< sal_Int8 >& >(aData).getArray(); + SvMemoryStream aMemStream( pData, aData.getLength(), StreamMode::READ ); + SbxBaseRef pBase = SbxBase::Load( aMemStream ); + return dynamic_cast<SbxObject*>(pBase.get()); +} + +// HACK! Because this value is defined in basctl/inc/vcsbxdef.hxx +// which we can't include here, we have to use the value directly +#define SBXID_DIALOG 101 + +namespace { + +class DialogContainer_Impl : public NameContainerHelper +{ + StarBASIC* mpLib; + +public: + explicit DialogContainer_Impl( StarBASIC* pLib ) + :mpLib( pLib ) {} + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; +}; + +} + +// Methods XElementAccess +uno::Type DialogContainer_Impl::getElementType() +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicDialogInfo>::get(); + return aModuleType; +} + +sal_Bool DialogContainer_Impl::hasElements() +{ + bool bRet = false; + + sal_Int32 nCount = mpLib->GetObjects()->Count(); + for( sal_Int32 nObj = 0; nObj < nCount ; nObj++ ) + { + SbxVariable* pVar = mpLib->GetObjects()->Get( nObj ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if ( pObj && (pObj->GetSbxId() == SBXID_DIALOG ) ) + { + bRet = true; + break; + } + } + return bRet; +} + +// Methods XNameAccess +uno::Any DialogContainer_Impl::getByName( const OUString& aName ) +{ + SbxVariable* pVar = mpLib->GetObjects()->Find( aName, SbxClassType::DontCare ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if( !( pObj && pObj->GetSbxId() == SBXID_DIALOG ) ) + { + throw container::NoSuchElementException(); + } + + uno::Reference< script::XStarBasicDialogInfo > xDialog = + new DialogInfo_Impl(aName, implGetDialogData(pObj)); + + uno::Any aRetAny; + aRetAny <<= xDialog; + return aRetAny; +} + +uno::Sequence< OUString > DialogContainer_Impl::getElementNames() +{ + sal_Int32 nCount = mpLib->GetObjects()->Count(); + uno::Sequence< OUString > aRetSeq( nCount ); + OUString* pRetSeq = aRetSeq.getArray(); + sal_Int32 nDialogCounter = 0; + + for( sal_Int32 nObj = 0; nObj < nCount ; nObj++ ) + { + SbxVariable* pVar = mpLib->GetObjects()->Get( nObj ); + SbxObject* pObj = dynamic_cast<SbxObject*> (pVar); + if ( pObj && ( pObj->GetSbxId() == SBXID_DIALOG ) ) + { + pRetSeq[ nDialogCounter ] = pVar->GetName(); + nDialogCounter++; + } + } + aRetSeq.realloc( nDialogCounter ); + return aRetSeq; +} + +sal_Bool DialogContainer_Impl::hasByName( const OUString& aName ) +{ + bool bRet = false; + SbxVariable* pVar = mpLib->GetObjects()->Find( aName, SbxClassType::DontCare ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if( pObj && ( pObj->GetSbxId() == SBXID_DIALOG ) ) + { + bRet = true; + } + return bRet; +} + + +// Methods XNameReplace +void DialogContainer_Impl::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + + +// Methods XNameContainer +void DialogContainer_Impl::insertByName( const OUString&, const uno::Any& aElement ) +{ + uno::Type aModuleType = cppu::UnoType<script::XStarBasicDialogInfo>::get(); + const uno::Type& aAnyType = aElement.getValueType(); + if( aModuleType != aAnyType ) + { + throw lang::IllegalArgumentException("types do not match", static_cast<cppu::OWeakObject*>(this), 2); + } + uno::Reference< script::XStarBasicDialogInfo > xMod; + aElement >>= xMod; + SbxObjectRef xDialog = implCreateDialog( xMod->getData() ); + mpLib->Insert( xDialog.get() ); +} + +void DialogContainer_Impl::removeByName( const OUString& Name ) +{ + SbxVariable* pVar = mpLib->GetObjects()->Find( Name, SbxClassType::DontCare ); + SbxObject* pObj = dynamic_cast<SbxObject*>(pVar); + if( !( pObj && ( pObj->GetSbxId() == SBXID_DIALOG ) ) ) + { + throw container::NoSuchElementException(); + } + mpLib->Remove( pVar ); +} + + +class LibraryContainer_Impl : public NameContainerHelper +{ + BasicManager* mpMgr; + +public: + explicit LibraryContainer_Impl( BasicManager* pMgr ) + :mpMgr( pMgr ) {} + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // Methods XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override; + + // Methods XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; +}; + + +// Methods XElementAccess +uno::Type LibraryContainer_Impl::getElementType() +{ + uno::Type aType = cppu::UnoType<script::XStarBasicLibraryInfo>::get(); + return aType; +} + +sal_Bool LibraryContainer_Impl::hasElements() +{ + sal_Int32 nLibs = mpMgr->GetLibCount(); + bool bRet = (nLibs > 0); + return bRet; +} + +// Methods XNameAccess +uno::Any LibraryContainer_Impl::getByName( const OUString& aName ) +{ + uno::Any aRetAny; + if( !mpMgr->HasLib( aName ) ) + throw container::NoSuchElementException(); + StarBASIC* pLib = mpMgr->GetLib( aName ); + + uno::Reference< container::XNameContainer > xModuleContainer = + new ModuleContainer_Impl( pLib ); + + uno::Reference< container::XNameContainer > xDialogContainer = + new DialogContainer_Impl( pLib ); + + BasicLibInfo* pLibInfo = mpMgr->FindLibInfo( pLib ); + + OUString aPassword = pLibInfo->GetPassword(); + + // TODO Only provide extern info! + OUString aExternaleSourceURL; + OUString aLinkTargetURL; + if( pLibInfo->IsReference() ) + { + aLinkTargetURL = pLibInfo->GetStorageName(); + } + else if( pLibInfo->IsExtern() ) + { + aExternaleSourceURL = pLibInfo->GetStorageName(); + } + uno::Reference< script::XStarBasicLibraryInfo > xLibInfo = new LibraryInfo_Impl + ( + aName, + xModuleContainer, + xDialogContainer, + aPassword, + aExternaleSourceURL, + aLinkTargetURL + ); + + aRetAny <<= xLibInfo; + return aRetAny; +} + +uno::Sequence< OUString > LibraryContainer_Impl::getElementNames() +{ + sal_uInt16 nLibs = mpMgr->GetLibCount(); + uno::Sequence< OUString > aRetSeq( nLibs ); + OUString* pRetSeq = aRetSeq.getArray(); + for( sal_uInt16 i = 0 ; i < nLibs ; i++ ) + { + pRetSeq[i] = mpMgr->GetLibName( i ); + } + return aRetSeq; +} + +sal_Bool LibraryContainer_Impl::hasByName( const OUString& aName ) +{ + bool bRet = mpMgr->HasLib( aName ); + return bRet; +} + +// Methods XNameReplace +void LibraryContainer_Impl::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + +// Methods XNameContainer +void LibraryContainer_Impl::insertByName( const OUString&, const uno::Any& ) +{ + // TODO: Insert a complete Library?! +} + +void LibraryContainer_Impl::removeByName( const OUString& Name ) +{ + StarBASIC* pLib = mpMgr->GetLib( Name ); + if( !pLib ) + { + throw container::NoSuchElementException(); + } + sal_uInt16 nLibId = mpMgr->GetLibId( Name ); + mpMgr->RemoveLib( nLibId ); +} + + +typedef WeakImplHelper< script::XStarBasicAccess > StarBasicAccessHelper; + + +class StarBasicAccess_Impl : public StarBasicAccessHelper +{ + BasicManager* mpMgr; + uno::Reference< container::XNameContainer > mxLibContainer; + +public: + explicit StarBasicAccess_Impl( BasicManager* pMgr ) + :mpMgr( pMgr ) {} + +public: + // Methods + virtual uno::Reference< container::XNameContainer > SAL_CALL getLibraryContainer() override; + virtual void SAL_CALL createLibrary( const OUString& LibName, const OUString& Password, + const OUString& ExternalSourceURL, const OUString& LinkTargetURL ) override; + virtual void SAL_CALL addModule( const OUString& LibraryName, const OUString& ModuleName, + const OUString& Language, const OUString& Source ) override; + virtual void SAL_CALL addDialog( const OUString& LibraryName, const OUString& DialogName, + const uno::Sequence< sal_Int8 >& Data ) override; +}; + +uno::Reference< container::XNameContainer > SAL_CALL StarBasicAccess_Impl::getLibraryContainer() +{ + if( !mxLibContainer.is() ) + mxLibContainer = new LibraryContainer_Impl( mpMgr ); + return mxLibContainer; +} + +void SAL_CALL StarBasicAccess_Impl::createLibrary +( + const OUString& LibName, + const OUString& Password, + const OUString&, + const OUString& LinkTargetURL +) +{ + StarBASIC* pLib = mpMgr->CreateLib( LibName, Password, LinkTargetURL ); + DBG_ASSERT( pLib, "XML Import: Basic library could not be created"); +} + +void SAL_CALL StarBasicAccess_Impl::addModule +( + const OUString& LibraryName, + const OUString& ModuleName, + const OUString&, + const OUString& Source +) +{ + StarBASIC* pLib = mpMgr->GetLib( LibraryName ); + DBG_ASSERT( pLib, "XML Import: Lib for module unknown"); + if( pLib ) + { + pLib->MakeModule( ModuleName, Source ); + } +} + +void SAL_CALL StarBasicAccess_Impl::addDialog +( + const OUString&, + const OUString&, + const uno::Sequence< sal_Int8 >& +) +{} + +// Basic XML Import/Export +uno::Reference< script::XStarBasicAccess > getStarBasicAccess( BasicManager* pMgr ) +{ + uno::Reference< script::XStarBasicAccess > xRet = + new StarBasicAccess_Impl( pMgr ); + return xRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/basmgr/vbahelper.cxx b/basic/source/basmgr/vbahelper.cxx new file mode 100644 index 000000000..caccb9bfc --- /dev/null +++ b/basic/source/basmgr/vbahelper.cxx @@ -0,0 +1,187 @@ +/* -*- 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 <basic/vbahelper.hxx> + +#include <map> +#include <mutex> +#include <vector> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <comphelper/processfactory.hxx> + +namespace basic::vba { + +using namespace ::com::sun::star; + + +namespace { + +/** Create an instance of a module manager. + */ +uno::Reference< frame::XModuleManager2 > lclCreateModuleManager() +{ + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext(), uno::UNO_SET_THROW ); + return frame::ModuleManager::create(xContext); +} + +typedef std::vector<uno::Reference<frame::XModel>> ModelVector; + +ModelVector CreateDocumentsEnumeration( + const uno::Reference< frame::XModel >& rxModel) +{ + ModelVector models; + try + { + uno::Reference< frame::XModuleManager2 > xModuleManager( lclCreateModuleManager() ); + OUString aIdentifier = xModuleManager->identify( rxModel ); + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); + uno::Reference< container::XEnumerationAccess > xComponentsEA( xDesktop->getComponents(), uno::UNO_SET_THROW ); + uno::Reference< container::XEnumeration > xEnumeration( xComponentsEA->createEnumeration(), uno::UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + uno::Reference< frame::XModel > xCurrModel( xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); + if( xModuleManager->identify( xCurrModel ) == aIdentifier ) + models.push_back( xCurrModel ); + } + } + catch(const uno::Exception& ) + { + } + return models; +} + +/** Locks or unlocks the controllers of the specified document model. + */ +void lclLockControllers( const uno::Reference< frame::XModel >& rxModel, bool bLockControllers ) +{ + if( rxModel.is() ) try + { + if( bLockControllers ) + rxModel->lockControllers(); + else + rxModel->unlockControllers(); + } + catch(const uno::Exception& ) + { + } +} + + +/** Enables or disables the container windows of all controllers of the + specified document model. + */ +void lclEnableContainerWindows( const uno::Reference< frame::XModel >& rxModel, bool bEnableWindows ) +{ + try + { + uno::Reference< frame::XModel2 > xModel2( rxModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XEnumeration > xControllersEnum( xModel2->getControllers(), uno::UNO_SET_THROW ); + // iterate over all controllers + while( xControllersEnum->hasMoreElements() ) + { + try + { + uno::Reference< frame::XController > xController( xControllersEnum->nextElement(), uno::UNO_QUERY_THROW ); + uno::Reference< frame::XFrame > xFrame( xController->getFrame(), uno::UNO_SET_THROW ); + uno::Reference< awt::XWindow > xWindow( xFrame->getContainerWindow(), uno::UNO_SET_THROW ); + xWindow->setEnable( bEnableWindows ); + } + catch(const uno::Exception& ) + { + } + } + } + catch(const uno::Exception& ) + { + } +} + + +typedef void (*ModifyDocumentFunc)( const uno::Reference< frame::XModel >&, bool ); + +/** Implementation iterating over all documents that have the same type as the + specified model, and calling the passed functor. + */ +void lclIterateDocuments( ModifyDocumentFunc pModifyDocumentFunc, const uno::Reference< frame::XModel >& rxModel, bool bModificator ) +{ + ModelVector models(CreateDocumentsEnumeration(rxModel)); + // iterate over all open documents + for (auto const& xCurrModel : models) + { + try + { + pModifyDocumentFunc(xCurrModel, bModificator); + } + catch (const uno::Exception&) + { + } + } +} + + +struct CurrDirPool +{ + std::mutex maMutex; + std::map< OUString, OUString > maCurrDirs; +}; + +} // namespace + + +void lockControllersOfAllDocuments( const uno::Reference< frame::XModel >& rxModel, bool bLockControllers ) +{ + lclIterateDocuments( &lclLockControllers, rxModel, bLockControllers ); +} + + +void enableContainerWindowsOfAllDocuments( const uno::Reference< frame::XModel >& rxModel, bool bEnableWindows ) +{ + lclIterateDocuments( &lclEnableContainerWindows, rxModel, bEnableWindows ); +} + + +void registerCurrentDirectory( const uno::Reference< frame::XModel >& rxModel, const OUString& rPath ) +{ + if( rPath.isEmpty() ) + return; + + static CurrDirPool StaticCurrDirPool; + + CurrDirPool& rPool = StaticCurrDirPool; + std::unique_lock aGuard( rPool.maMutex ); + try + { + uno::Reference< frame::XModuleManager2 > xModuleManager( lclCreateModuleManager() ); + OUString aIdentifier = xModuleManager->identify( rxModel ); + if( !aIdentifier.isEmpty() ) + rPool.maCurrDirs[ aIdentifier ] = rPath; + } + catch(const uno::Exception& ) + { + } +} + + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |