diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /basic/source/uno | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | basic/source/uno/dlgcont.cxx | 584 | ||||
-rw-r--r-- | basic/source/uno/modsizeexceeded.cxx | 59 | ||||
-rw-r--r-- | basic/source/uno/namecont.cxx | 3462 | ||||
-rw-r--r-- | basic/source/uno/scriptcont.cxx | 1230 |
4 files changed, 5335 insertions, 0 deletions
diff --git a/basic/source/uno/dlgcont.cxx b/basic/source/uno/dlgcont.cxx new file mode 100644 index 000000000..eee4e8300 --- /dev/null +++ b/basic/source/uno/dlgcont.cxx @@ -0,0 +1,584 @@ +/* -*- 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 <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XInputStreamProvider.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/resource/StringResourceWithStorage.hpp> +#include <com/sun/star/resource/StringResourceWithLocation.hpp> +#include <com/sun/star/document/GraphicStorageHandler.hpp> +#include <com/sun/star/document/XGraphicStorageHandler.hpp> +#include <dlgcont.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/processfactory.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <xmlscript/xmldlg_imexp.hxx> +#include <sot/storage.hxx> +#include <svtools/sfxecode.hxx> +#include <svtools/ehdl.hxx> +#include <vcl/GraphicObject.hxx> +#include <i18nlangtag/languagetag.hxx> + +namespace basic +{ + +using namespace com::sun::star::document; +using namespace com::sun::star::container; +using namespace com::sun::star::io; +using namespace com::sun::star::uno; +using namespace com::sun::star::ucb; +using namespace com::sun::star::lang; +using namespace com::sun::star::script; +using namespace com::sun::star::xml::sax; +using namespace com::sun::star; +using namespace cppu; + +using com::sun::star::uno::Reference; + + +// Implementation class SfxDialogLibraryContainer + +const char* SfxDialogLibraryContainer::getInfoFileName() const { return "dialog"; } +const char* SfxDialogLibraryContainer::getOldInfoFileName() const { return "dialogs"; } +const char* SfxDialogLibraryContainer::getLibElementFileExtension() const { return "xdl"; } +const char* SfxDialogLibraryContainer::getLibrariesDir() const { return "Dialogs"; } + +// Ctor for service +SfxDialogLibraryContainer::SfxDialogLibraryContainer() +{ + // all initialisation has to be done + // by calling XInitialization::initialize +} + +SfxDialogLibraryContainer::SfxDialogLibraryContainer( const uno::Reference< embed::XStorage >& xStorage ) +{ + init( OUString(), xStorage ); +} + +// Methods to get library instances of the correct type +SfxLibrary* SfxDialogLibraryContainer::implCreateLibrary( const OUString& aName ) +{ + SfxLibrary* pRet = new SfxDialogLibrary( maModifiable, aName, mxSFI, this ); + return pRet; +} + +SfxLibrary* SfxDialogLibraryContainer::implCreateLibraryLink + ( const OUString& aName, const OUString& aLibInfoFileURL, + const OUString& StorageURL, bool ReadOnly ) +{ + SfxLibrary* pRet = new SfxDialogLibrary + ( maModifiable, aName, mxSFI, aLibInfoFileURL, StorageURL, ReadOnly, this ); + return pRet; +} + +Any SfxDialogLibraryContainer::createEmptyLibraryElement() +{ + Reference< XInputStreamProvider > xISP; + Any aRetAny; + aRetAny <<= xISP; + return aRetAny; +} + +bool SfxDialogLibraryContainer::isLibraryElementValid(const Any& rElement) const +{ + return SfxDialogLibrary::containsValidDialog(rElement); +} + +static bool writeOasis2OOoLibraryElement( + const Reference< XInputStream >& xInput, const Reference< XOutputStream >& xOutput ) +{ + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + + Reference< lang::XMultiComponentFactory > xSMgr( + xContext->getServiceManager() ); + + Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext); + + Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext); + + xWriter->setOutputStream( xOutput ); + + Sequence<Any> aArgs( 1 ); + aArgs[0] <<= xWriter; + + Reference< xml::sax::XDocumentHandler > xHandler( + xSMgr->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.Oasis2OOoTransformer", + aArgs, xContext ), + UNO_QUERY ); + + xParser->setDocumentHandler( xHandler ); + + xml::sax::InputSource source; + source.aInputStream = xInput; + source.sSystemId = "virtual file"; + + xParser->parseStream( source ); + + return true; +} + +void SfxDialogLibraryContainer::writeLibraryElement +( + const Reference < XNameContainer >& xLib, + const OUString& aElementName, + const Reference< XOutputStream >& xOutput +) +{ + Any aElement = xLib->getByName( aElementName ); + Reference< XInputStreamProvider > xISP; + aElement >>= xISP; + if( !xISP.is() ) + return; + + Reference< XInputStream > xInput( xISP->createInputStream() ); + + bool bComplete = false; + if ( mbOasis2OOoFormat ) + { + bComplete = writeOasis2OOoLibraryElement( xInput, xOutput ); + } + + if ( !bComplete ) + { + Sequence< sal_Int8 > bytes; + sal_Int32 nRead = xInput->readBytes( bytes, xInput->available() ); + for (;;) + { + if( nRead ) + xOutput->writeBytes( bytes ); + + nRead = xInput->readBytes( bytes, 1024 ); + if (! nRead) + break; + } + } + xInput->closeInput(); +} + +void SfxDialogLibraryContainer::storeLibrariesToStorage( const uno::Reference< embed::XStorage >& xStorage ) +{ + LibraryContainerMethodGuard aGuard( *this ); + mbOasis2OOoFormat = false; + + if ( mxStorage.is() && xStorage.is() ) + { + try + { + long nSource = SotStorage::GetVersion( mxStorage ); + long nTarget = SotStorage::GetVersion( xStorage ); + + if ( nSource == SOFFICE_FILEFORMAT_CURRENT && + nTarget != SOFFICE_FILEFORMAT_CURRENT ) + { + mbOasis2OOoFormat = true; + } + } + catch (const Exception& ) + { + TOOLS_WARN_EXCEPTION("basic", ""); + // if we cannot get the version then the + // Oasis2OOoTransformer will not be used + assert(false); + } + } + + SfxLibraryContainer::storeLibrariesToStorage( xStorage ); + + // we need to export out any embedded image object(s) + // associated with any Dialogs. First, we need to actually gather any such urls + // for each dialog in this container + const Sequence< OUString > sLibraries = getElementNames(); + for ( const OUString& rName : sLibraries ) + { + loadLibrary( rName ); + Reference< XNameContainer > xLib; + getByName( rName ) >>= xLib; + if ( xLib.is() ) + { + Sequence< OUString > sDialogs = xLib->getElementNames(); + sal_Int32 nDialogs( sDialogs.getLength() ); + for ( sal_Int32 j=0; j < nDialogs; ++j ) + { + // Each Dialog has an associated xISP + Reference< io::XInputStreamProvider > xISP; + xLib->getByName( sDialogs[ j ] ) >>= xISP; + if ( xISP.is() ) + { + Reference< io::XInputStream > xInput( xISP->createInputStream() ); + Reference< XNameContainer > xDialogModel( + mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", mxContext), + UNO_QUERY ); + ::xmlscript::importDialogModel( xInput, xDialogModel, mxContext, mxOwnerDocument ); + std::vector<uno::Reference<graphic::XGraphic>> vxGraphicList; + vcl::graphic::SearchForGraphics(Reference<XInterface>(xDialogModel, UNO_QUERY), vxGraphicList); + if (!vxGraphicList.empty()) + { + // Export the images to the storage + Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + xGraphicStorageHandler.set(document::GraphicStorageHandler::createWithStorage(mxContext, xStorage)); + if (xGraphicStorageHandler.is()) + { + for (uno::Reference<graphic::XGraphic> const & rxGraphic : vxGraphicList) + { + xGraphicStorageHandler->saveGraphic(rxGraphic); + } + } + } + } + } + } + } + mbOasis2OOoFormat = false; +} + + +Any SfxDialogLibraryContainer::importLibraryElement + ( const Reference < XNameContainer >& /*xLib*/, + const OUString& /*aElementName */, const OUString& aFile, + const uno::Reference< io::XInputStream >& xElementStream ) +{ + Any aRetAny; + + // TODO: Member because later it will be a component + //Reference< XMultiServiceFactory > xMSF( comphelper::getProcessServiceFactory() ); + //if( !xMSF.is() ) + //{ + // OSL_FAIL( "### couldn't get ProcessServiceFactory" ); + // return aRetAny; + //} + + Reference< XParser > xParser = xml::sax::Parser::create( mxContext ); + + Reference< XNameContainer > xDialogModel( + mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", mxContext), + UNO_QUERY ); + if( !xDialogModel.is() ) + { + OSL_FAIL( "### couldn't create com.sun.star.awt.UnoControlDialogModel component" ); + return aRetAny; + } + + // Read from storage? + bool bStorage = xElementStream.is(); + Reference< XInputStream > xInput; + + if( bStorage ) + { + xInput = xElementStream; + } + else + { + try + { + xInput = mxSFI->openFileRead( aFile ); + } + catch(const Exception& ) + //catch( Exception& e ) + { + // TODO: + //throw WrappedTargetException( e ); + } + } + if( !xInput.is() ) + return aRetAny; + + InputSource source; + source.aInputStream = xInput; + source.sSystemId = aFile; + + try { + // start parsing + xParser->setDocumentHandler( ::xmlscript::importDialogModel( xDialogModel, mxContext, mxOwnerDocument ) ); + xParser->parseStream( source ); + } + catch(const Exception& ) + { + OSL_FAIL( "Parsing error" ); + SfxErrorContext aEc( ERRCTX_SFX_LOADBASIC, aFile ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + return aRetAny; + } + + // Create InputStream, TODO: Implement own InputStreamProvider + // to avoid creating the DialogModel here! + Reference< XInputStreamProvider > xISP = ::xmlscript::exportDialogModel( xDialogModel, mxContext, mxOwnerDocument ); + aRetAny <<= xISP; + return aRetAny; +} + +void SfxDialogLibraryContainer::importFromOldStorage( const OUString& ) +{ + // Nothing to do here, old dialogs cannot be imported +} + +SfxLibraryContainer* SfxDialogLibraryContainer::createInstanceImpl() +{ + return new SfxDialogLibraryContainer(); +} + +const char aResourceFileNameBase[] = "DialogStrings"; +const char aResourceFileCommentBase[] = "# Strings for Dialog Library "; + +// Resource handling +Reference< css::resource::XStringResourcePersistence > + SfxDialogLibraryContainer::implCreateStringResource( SfxDialogLibrary* pDialogLibrary ) +{ + Reference< resource::XStringResourcePersistence > xRet; + OUString aLibName = pDialogLibrary->getName(); + bool bReadOnly = pDialogLibrary->mbReadOnly; + + // get ui locale + ::com::sun ::star::lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale(); + + OUString aComment= aResourceFileCommentBase + aLibName; + + bool bStorage = mxStorage.is(); + if( bStorage ) + { + uno::Reference< embed::XStorage > xLibrariesStor; + uno::Reference< embed::XStorage > xLibraryStor; + try { + xLibrariesStor = mxStorage->openStorageElement( maLibrariesDir, embed::ElementModes::READ ); + // TODO: Should be READWRITE with new storage concept using store() instead of storeTo() + if ( !xLibrariesStor.is() ) + throw uno::RuntimeException("null returned from openStorageElement"); + + xLibraryStor = xLibrariesStor->openStorageElement( aLibName, embed::ElementModes::READ ); + // TODO: Should be READWRITE with new storage concept using store() instead of storeTo() + if ( !xLibraryStor.is() ) + throw uno::RuntimeException("null returned from openStorageElement"); + } + catch(const uno::Exception& ) + { + // Something went wrong while trying to get the storage library. + // Return an object that supports StringResourceWithStorage, give it a storage location later. + xRet = Reference< resource::XStringResourcePersistence >( + mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.resource.StringResourceWithStorage", mxContext), + UNO_QUERY ); + return xRet; + } + + xRet = resource::StringResourceWithStorage::create(mxContext, xLibraryStor, bReadOnly, aLocale, aResourceFileNameBase, aComment); + } + else + { + OUString aLocation = createAppLibraryFolder( pDialogLibrary, aLibName ); + // TODO: Real handler? + Reference< task::XInteractionHandler > xDummyHandler; + + xRet = resource::StringResourceWithLocation::create(mxContext, aLocation, bReadOnly, aLocale, aResourceFileNameBase, aComment, xDummyHandler); + } + + return xRet; +} + +void SfxDialogLibraryContainer::onNewRootStorage() +{ + // 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 ]; + SfxDialogLibrary* pDialogLibrary = static_cast<SfxDialogLibrary*>( getImplLib( aName ) ); + + Reference< resource::XStringResourcePersistence > xStringResourcePersistence = + pDialogLibrary->getStringResourcePersistence(); + + if( xStringResourcePersistence.is() ) + { + Reference< embed::XStorage > xLibrariesStor; + Reference< embed::XStorage > xLibraryStor; + try { + xLibrariesStor = mxStorage->openStorageElement( maLibrariesDir, embed::ElementModes::READWRITE ); + if ( !xLibrariesStor.is() ) + throw uno::RuntimeException("null returned from openStorageElement"); + + OUString aLibName = pDialogLibrary->getName(); + xLibraryStor = xLibrariesStor->openStorageElement( aLibName, embed::ElementModes::READWRITE ); + if ( !xLibraryStor.is() ) + throw uno::RuntimeException("null returned from openStorageElement"); + + Reference< resource::XStringResourceWithStorage > + xStringResourceWithStorage( xStringResourcePersistence, UNO_QUERY ); + if( xStringResourceWithStorage.is() ) + xStringResourceWithStorage->setStorage( xLibraryStor ); + } + catch(const uno::Exception& ) + { + // TODO: Error handling? + } + } + } +} + +sal_Bool SAL_CALL +SfxDialogLibraryContainer:: HasExecutableCode( const OUString& /*Library*/ ) +{ + return false; // dialog library has no executable code +} + +// Service + +OUString SAL_CALL SfxDialogLibraryContainer::getImplementationName( ) +{ + return "com.sun.star.comp.sfx2.DialogLibraryContainer"; +} + +Sequence< OUString > SAL_CALL SfxDialogLibraryContainer::getSupportedServiceNames( ) +{ + return {"com.sun.star.script.DocumentDialogLibraryContainer", + "com.sun.star.script.DialogLibraryContainer"}; // for compatibility +} + +// Implementation class SfxDialogLibrary + +// Ctor +SfxDialogLibrary::SfxDialogLibrary( ModifiableHelper& _rModifiable, + const OUString& aName, + const Reference< XSimpleFileAccess3 >& xSFI, + SfxDialogLibraryContainer* pParent ) + : SfxLibrary( _rModifiable, cppu::UnoType<XInputStreamProvider>::get(), xSFI ) + , m_pParent( pParent ) + , m_aName( aName ) +{ +} + +SfxDialogLibrary::SfxDialogLibrary( ModifiableHelper& _rModifiable, + const OUString& aName, + const Reference< XSimpleFileAccess3 >& xSFI, + const OUString& aLibInfoFileURL, + const OUString& aStorageURL, + bool ReadOnly, + SfxDialogLibraryContainer* pParent ) + : SfxLibrary( _rModifiable, cppu::UnoType<XInputStreamProvider>::get(), + xSFI, aLibInfoFileURL, aStorageURL, ReadOnly) + , m_pParent( pParent ) + , m_aName( aName ) +{ +} + +IMPLEMENT_FORWARD_XINTERFACE2( SfxDialogLibrary, SfxLibrary, SfxDialogLibrary_BASE ); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( SfxDialogLibrary, SfxLibrary, SfxDialogLibrary_BASE ); + +// Provide modify state including resources +bool SfxDialogLibrary::isModified() +{ + bool bRet = implIsModified(); + + if( !bRet && m_xStringResourcePersistence.is() ) + bRet = m_xStringResourcePersistence->isModified(); + // else: Resources not accessed so far -> not modified + + return bRet; +} + +void SfxDialogLibrary::storeResources() +{ + if( m_xStringResourcePersistence.is() ) + m_xStringResourcePersistence->store(); +} + +void SfxDialogLibrary::storeResourcesAsURL + ( const OUString& URL, const OUString& NewName ) +{ + OUString aComment(aResourceFileCommentBase); + m_aName = NewName; + aComment += m_aName; + + if( m_xStringResourcePersistence.is() ) + { + m_xStringResourcePersistence->setComment( aComment ); + + Reference< resource::XStringResourceWithLocation > + xStringResourceWithLocation( m_xStringResourcePersistence, UNO_QUERY ); + if( xStringResourceWithLocation.is() ) + xStringResourceWithLocation->storeAsURL( URL ); + } +} + +void SfxDialogLibrary::storeResourcesToURL( const OUString& URL, + const Reference< task::XInteractionHandler >& xHandler ) +{ + OUString aComment = aResourceFileCommentBase + m_aName; + + if( m_xStringResourcePersistence.is() ) + { + m_xStringResourcePersistence->storeToURL + ( URL, aResourceFileNameBase, aComment, xHandler ); + } +} + +void SfxDialogLibrary::storeResourcesToStorage( const css::uno::Reference< css::embed::XStorage >& xStorage ) +{ + OUString aComment = aResourceFileCommentBase + m_aName; + + if( m_xStringResourcePersistence.is() ) + { + m_xStringResourcePersistence->storeToStorage + ( xStorage, aResourceFileNameBase, aComment ); + } +} + + +// XStringResourceSupplier +Reference< resource::XStringResourceResolver > + SAL_CALL SfxDialogLibrary::getStringResource( ) +{ + if( !m_xStringResourcePersistence.is() ) + m_xStringResourcePersistence = m_pParent->implCreateStringResource( this ); + + return m_xStringResourcePersistence; +} + +bool SfxDialogLibrary::containsValidDialog( const css::uno::Any& aElement ) +{ + Reference< XInputStreamProvider > xISP; + aElement >>= xISP; + return xISP.is(); +} + +bool SfxDialogLibrary::isLibraryElementValid(const css::uno::Any& rElement) const +{ + return SfxDialogLibrary::containsValidDialog(rElement); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_sfx2_DialogLibraryContainer_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new basic::SfxDialogLibraryContainer()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/uno/modsizeexceeded.cxx b/basic/source/uno/modsizeexceeded.cxx new file mode 100644 index 000000000..a54b426c7 --- /dev/null +++ b/basic/source/uno/modsizeexceeded.cxx @@ -0,0 +1,59 @@ +/* -*- 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/modsizeexceeded.hxx> + +#include <comphelper/interaction.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/script/ModuleSizeExceededRequest.hpp> + +using namespace com::sun::star; +using namespace cppu; +using namespace osl; + +ModuleSizeExceeded::ModuleSizeExceeded( const std::vector< OUString >& sModules ) +{ + script::ModuleSizeExceededRequest aReq; + aReq.Names = comphelper::containerToSequence(sModules); + + m_aRequest <<= aReq; + + m_xAbort = new comphelper::OInteractionAbort; + m_xApprove = new comphelper::OInteractionApprove; + m_lContinuations.realloc( 2 ); + m_lContinuations[0] = m_xApprove; + m_lContinuations[1] = m_xAbort; +} + +bool +ModuleSizeExceeded::isAbort() const +{ + comphelper::OInteractionAbort* pBase = static_cast< comphelper::OInteractionAbort* >( m_xAbort.get() ); + return pBase->wasSelected(); +} + +bool +ModuleSizeExceeded::isApprove() const +{ + comphelper::OInteractionApprove* pBase = static_cast< comphelper::OInteractionApprove* >( m_xApprove.get() ); + return pBase->wasSelected(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/uno/namecont.cxx b/basic/source/uno/namecont.cxx new file mode 100644 index 000000000..0904dcb76 --- /dev/null +++ b/basic/source/uno/namecont.cxx @@ -0,0 +1,3462 @@ +/* -*- 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/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 <vcl/svapp.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> + +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(); + } + 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.realloc( 1 ); + aEvent.Changes[ 0 ].Accessor <<= aName; + aEvent.Changes[ 0 ].Element = aElement; + aEvent.Changes[ 0 ].ReplacedElement = 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(); + } + + 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.realloc( 1 ); + aEvent.Changes[ 0 ].Accessor <<= aName; + aEvent.Changes[ 0 ].Element = 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.realloc( 1 ); + aEvent.Changes[ 0 ].Accessor <<= aName; + // aEvent.Changes[ 0 ].Element remains empty (meaning "replaced with nothing") + aEvent.Changes[ 0 ].ReplacedElement = 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"); + } + maContainerListeners.addInterface( Reference<XInterface>(xListener, UNO_QUERY) ); +} + +void SAL_CALL NameContainer::removeContainerListener( const Reference< XContainerListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("removeContainerListener called with null xListener"); + } + maContainerListeners.removeInterface( Reference<XInterface>(xListener, UNO_QUERY) ); +} + +// Methods XChangesNotifier +void SAL_CALL NameContainer::addChangesListener( const Reference< XChangesListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("addChangesListener called with null xListener"); + } + maChangesListeners.addInterface( Reference<XInterface>(xListener, UNO_QUERY) ); +} + +void SAL_CALL NameContainer::removeChangesListener( const Reference< XChangesListener >& xListener ) +{ + if( !xListener.is() ) + { + throw RuntimeException("removeChangesListener called with null xListener"); + } + maChangesListeners.removeInterface( Reference<XInterface>(xListener, UNO_QUERY) ); +} + + +// 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 ); +} + + +VBAScriptListenerContainer::VBAScriptListenerContainer( ::osl::Mutex& rMutex ) : + VBAScriptListenerContainer_BASE( rMutex ) +{ +} + +bool VBAScriptListenerContainer::implTypedNotify( const Reference< vba::XVBAScriptListener >& rxListener, const vba::VBAScriptEvent& rEvent ) +{ + rxListener->notifyVBAScriptEvent( rEvent ); + return true; // notify all other listeners too +} + +// Ctor +SfxLibraryContainer::SfxLibraryContainer() + : SfxLibraryContainer_BASE( m_aMutex ) + , maVBAScriptListeners( m_aMutex ) + , mnRunningVBAScripts( 0 ) + , mbVBACompat( false ) + , 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(); + } + mxStorage = _rxRootStorage; + onNewRootStorage(); +} + +void SAL_CALL SfxLibraryContainer::storeLibrariesToStorage( const Reference< XStorage >& _rxRootStorage ) +{ + LibraryContainerMethodGuard aGuard( *this ); + if ( !_rxRootStorage.is() ) + { + throw IllegalArgumentException(); + } + 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, + const OUString& rCheckFileName, + const OUString& 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, const OUString& rLibName, + const OUString& rInfoFileName, bool bUser ) +{ + if( bUser ) + { + rStr = "$(USER)/basic/"; + } + else + { + rStr = "$(INST)/" LIBO_SHARE_FOLDER "/basic/"; + } + rStr += 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 = OUString::createFromAscii( getInfoFileName() ); + maOldInfoFileName = OUString::createFromAscii( getOldInfoFileName() ); + maLibElementFileExtension = OUString::createFromAscii( getLibElementFileExtension() ); + maLibrariesDir = OUString::createFromAscii( 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(); + } + + Reference< XParser > xParser = xml::sax::Parser::create(mxContext); + + 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( maLibraryPath.getToken(0, ';') )); + } + else + { + pLibInfoInetObj.reset(new INetURLObject( maLibraryPath.getToken(1, ';') )); + } + pLibInfoInetObj->insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + pLibInfoInetObj->setExtension( "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( maLibraryPath.getToken(1, ';') ); + aLibInfoInetObj.insertName( maOldInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aLibInfoInetObj.setExtension( "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 + std::unique_ptr< ::xmlscript::LibDescriptorArray> pLibArray(new ::xmlscript::LibDescriptorArray()); + + 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( maLibraryPath.getToken(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; + + INetURLObject aUserBasicInetObj( maLibraryPath.getToken(1, ';') ); + OUString aStandardStr("Standard"); + + INetURLObject aPrevUserBasicInetObj_1( aUserBasicInetObj ); + aPrevUserBasicInetObj_1.removeSegment(); + INetURLObject aPrevUserBasicInetObj_2 = aPrevUserBasicInetObj_1; + aPrevUserBasicInetObj_1.Append( "__basic_80" ); + aPrevUserBasicInetObj_2.Append( "__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, "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( "__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( "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( "__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( "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, const OUString& aName ) +{ + OUString aLibDirPath = pLib->maStorageURL; + if( aLibDirPath.isEmpty() ) + { + INetURLObject aInetObj( maLibraryPath.getToken(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, + const OUString& aName, + const uno::Reference< embed::XStorage >& xStorage ) +{ + Reference< XSimpleFileAccess3 > xDummySFA; + Reference< XInteractionHandler > xDummyHandler; + implStoreLibrary( pLib, aName, xStorage, OUString(), xDummySFA, xDummyHandler ); +} + +// New variant for library export +void SfxLibraryContainer::implStoreLibrary( SfxLibrary* pLib, + const OUString& aName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& 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.isEmpty(); + 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, OUString(), xDummySFA ); +} + +// New variant for library export +void SfxLibraryContainer::implStoreLibraryIndexFile( SfxLibrary* pLib, + const ::xmlscript::LibDescriptor& rLib, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& 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.isEmpty(); + 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( "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; + std::unique_ptr< ::xmlscript::LibDescriptorArray > pLibArray(new ::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( maLibraryPath.getToken(1, ';') ); + aLibInfoInetObj.insertName( maInfoFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + aLibInfoInetObj.setExtension( "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 ); + SfxLibrary* pNewLib = implCreateLibrary( Name ); + pNewLib->maLibElementFileExtension = maLibElementFileExtension; + + createVariableURL( pNewLib->maUnexpandedStorageURL, Name, maInfoFileName, true ); + + Reference< XNameAccess > xNameAccess = static_cast< 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 ); + + + 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, aLibDesc, xDummyStor, OUString() ); + implImportLibDescriptor( pNewLib, aLibDesc ); + + Reference< XNameAccess > xRet = static_cast< XNameAccess* >( 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(); + } + // 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( maLibraryPath.getToken(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"); + } + + 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"); + } +#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(); + } + 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( maLibraryPath.getToken(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 ) + { + OUString sInitialDocumentURL; + Reference< XStorageBasedDocument > xDocument; + if ( _rArguments[0] >>= sInitialDocumentURL ) + { + init( sInitialDocumentURL, nullptr ); + return; + } + + if ( _rArguments[0] >>= xDocument ) + { + initializeFromDocument( xDocument ); + return; + } + } + + throw IllegalArgumentException(); +} + +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(); + } + 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.get() ); + maVBAScriptListeners.disposing( 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(); + } + 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.addTypedListener( rxListener ); +} + +void SAL_CALL SfxLibraryContainer::removeVBAScriptListener( const Reference< vba::XVBAScriptListener >& rxListener ) +{ + maVBAScriptListeners.removeTypedListener( 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.notify( aEvent ); +} + +// 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, + const OUString& aLibInfoFileURL, const OUString& aStorageURL, bool ReadOnly ) + : OComponentHelper( m_aMutex ) + , mxSFI( xSFI ) + , mrModifiable( _rModifiable ) + , maNameContainer( new NameContainer(aType) ) + , mbLoaded( false ) + , mbIsModified( true ) + , mbInitialised( false ) + , maLibInfoFileURL( aLibInfoFileURL ) + , maStorageURL( 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 + +#define sBasicLibMediaType "application/vnd.sun.star.basic-library" +#define sDialogLibMediaType "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: */ diff --git a/basic/source/uno/scriptcont.cxx b/basic/source/uno/scriptcont.cxx new file mode 100644 index 000000000..c9b8fdeb0 --- /dev/null +++ b/basic/source/uno/scriptcont.cxx @@ -0,0 +1,1230 @@ +/* -*- 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 <scriptcont.hxx> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XEncryptionProtectedSource.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/task/ErrorCodeIOException.hpp> +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <comphelper/storagehelper.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <sal/log.hxx> +#include <sot/storage.hxx> + +// For password functionality +#include <tools/urlobj.hxx> + + +#include <svtools/sfxecode.hxx> +#include <svtools/ehdl.hxx> +#include <basic/basmgr.hxx> +#include <basic/sbmod.hxx> +#include <basic/modsizeexceeded.hxx> +#include <xmlscript/xmlmod_imexp.hxx> +#include <com/sun/star/util/VetoException.hpp> +#include <memory> + +namespace basic +{ + +using namespace com::sun::star::document; +using namespace com::sun::star::container; +using namespace com::sun::star::io; +using namespace com::sun::star::uno; +using namespace com::sun::star::ucb; +using namespace com::sun::star::lang; +using namespace com::sun::star::script; +using namespace com::sun::star::xml::sax; +using namespace com::sun::star; +using namespace cppu; +using namespace osl; + + +// Implementation class SfxScriptLibraryContainer + +const char* SfxScriptLibraryContainer::getInfoFileName() const { return "script"; } +const char* SfxScriptLibraryContainer::getOldInfoFileName() const { return "script"; } +const char* SfxScriptLibraryContainer::getLibElementFileExtension() const { return "xba"; } +const char* SfxScriptLibraryContainer::getLibrariesDir() const { return "Basic"; } + +// OldBasicPassword interface +void SfxScriptLibraryContainer::setLibraryPassword( const OUString& rLibraryName, const OUString& rPassword ) +{ + try + { + SfxLibrary* pImplLib = getImplLib( rLibraryName ); + if( !rPassword.isEmpty() ) + { + pImplLib->mbDoc50Password = true; + pImplLib->mbPasswordProtected = true; + pImplLib->maPassword = rPassword; + SfxScriptLibrary *const pSL(dynamic_cast<SfxScriptLibrary *>(pImplLib)); + if (pSL && pSL->mbLoaded) + { + pSL->mbLoadedSource = true; // must store source code now! + } + } + } + catch(const NoSuchElementException& ) {} +} + +// Ctor for service +SfxScriptLibraryContainer::SfxScriptLibraryContainer() +{ + // all initialisation has to be done + // by calling XInitialization::initialize +} + +SfxScriptLibraryContainer::SfxScriptLibraryContainer( const uno::Reference< embed::XStorage >& xStorage ) +{ + init( OUString(), xStorage ); +} + +// Methods to get library instances of the correct type +SfxLibrary* SfxScriptLibraryContainer::implCreateLibrary( const OUString& ) +{ + SfxLibrary* pRet = new SfxScriptLibrary( maModifiable, mxSFI ); + return pRet; +} + +SfxLibrary* SfxScriptLibraryContainer::implCreateLibraryLink( const OUString&, + const OUString& aLibInfoFileURL, + const OUString& StorageURL, + bool ReadOnly ) +{ + SfxLibrary* pRet = new SfxScriptLibrary( maModifiable, mxSFI, + aLibInfoFileURL, StorageURL, ReadOnly ); + return pRet; +} + +Any SfxScriptLibraryContainer::createEmptyLibraryElement() +{ + Any aRetAny; + aRetAny <<= OUString(); + return aRetAny; +} + +bool SfxScriptLibraryContainer::isLibraryElementValid(const Any& rElement) const +{ + return SfxScriptLibrary::containsValidModule(rElement); +} + +void SfxScriptLibraryContainer::writeLibraryElement( const Reference < XNameContainer >& xLib, + const OUString& aElementName, + const Reference< XOutputStream >& xOutput) +{ + // Create sax writer + Reference< XWriter > xWriter = xml::sax::Writer::create(mxContext); + + Reference< XTruncate > xTruncate( xOutput, UNO_QUERY ); + OSL_ENSURE( xTruncate.is(), "Currently only the streams that can be truncated are expected!" ); + if ( xTruncate.is() ) + { + xTruncate->truncate(); + } + xWriter->setOutputStream( xOutput ); + + xmlscript::ModuleDescriptor aMod; + aMod.aName = aElementName; + aMod.aLanguage = "StarBasic"; + Any aElement = xLib->getByName( aElementName ); + aElement >>= aMod.aCode; + + Reference< script::vba::XVBAModuleInfo > xModInfo( xLib, UNO_QUERY ); + if( xModInfo.is() && xModInfo->hasModuleInfo( aElementName ) ) + { + script::ModuleInfo aModInfo = xModInfo->getModuleInfo( aElementName ); + switch( aModInfo.ModuleType ) + { + case ModuleType::NORMAL: + aMod.aModuleType = "normal"; + break; + case ModuleType::CLASS: + aMod.aModuleType ="class"; + break; + case ModuleType::FORM: + aMod.aModuleType = "form"; + break; + case ModuleType::DOCUMENT: + aMod.aModuleType = "document"; + break; + case ModuleType::UNKNOWN: + // nothing + break; + } + } + + xmlscript::exportScriptModule( xWriter, aMod ); +} + + +Any SfxScriptLibraryContainer::importLibraryElement + ( const Reference < XNameContainer >& xLib, + const OUString& aElementName, const OUString& aFile, + const uno::Reference< io::XInputStream >& xInStream ) +{ + Any aRetAny; + + Reference< XParser > xParser = xml::sax::Parser::create( mxContext ); + + // Read from storage? + bool bStorage = xInStream.is(); + Reference< XInputStream > xInput; + + if( bStorage ) + { + xInput = xInStream; + } + else + { + try + { + xInput = mxSFI->openFileRead( aFile ); + } + catch(const Exception& ) + //catch( Exception& e ) + { + // TODO: + //throw WrappedTargetException( e ); + } + } + + if( !xInput.is() ) + return aRetAny; + + InputSource source; + source.aInputStream = xInput; + source.sSystemId = aFile; + + // start parsing + xmlscript::ModuleDescriptor aMod; + + try + { + xParser->setDocumentHandler( ::xmlscript::importScriptModule( aMod ) ); + xParser->parseStream( source ); + } + catch(const Exception& ) + { + SfxErrorContext aEc( ERRCTX_SFX_LOADBASIC, aFile ); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + + aRetAny <<= aMod.aCode; + + // TODO: Check language + // aMod.aLanguage + // aMod.aName ignored + if( !aMod.aModuleType.isEmpty() ) + { + /* 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( getVBACompatibilityMode() ) try + { + Reference< frame::XModel > xModel( mxOwnerDocument ); // weak-ref -> ref + Reference< XMultiServiceFactory > xFactory( xModel, UNO_QUERY_THROW ); + xFactory->createInstance("ooo.vba.VBAGlobals"); + } + catch(const Exception& ) + { + } + + script::ModuleInfo aModInfo; + aModInfo.ModuleType = ModuleType::UNKNOWN; + if( aMod.aModuleType == "normal" ) + { + aModInfo.ModuleType = ModuleType::NORMAL; + } + else if( aMod.aModuleType == "class" ) + { + aModInfo.ModuleType = ModuleType::CLASS; + } + else if( aMod.aModuleType == "form" ) + { + aModInfo.ModuleType = ModuleType::FORM; + aModInfo.ModuleObject = mxOwnerDocument; + } + else if( aMod.aModuleType == "document" ) + { + aModInfo.ModuleType = ModuleType::DOCUMENT; + + // #163691# use the same codename access instance for all document modules + if( !mxCodeNameAccess.is() ) try + { + Reference<frame::XModel > xModel( mxOwnerDocument ); + Reference< XMultiServiceFactory> xSF( xModel, UNO_QUERY_THROW ); + mxCodeNameAccess.set( xSF->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), UNO_QUERY ); + } + catch(const Exception& ) {} + + if( mxCodeNameAccess.is() ) + { + try + { + aModInfo.ModuleObject.set( mxCodeNameAccess->getByName( aElementName), uno::UNO_QUERY ); + } + catch(const uno::Exception&) + { + SAL_WARN("basic", "Failed to get document object for " << aElementName ); + } + } + } + + Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, UNO_QUERY ); + if( xVBAModuleInfo.is() ) + { + if( xVBAModuleInfo->hasModuleInfo( aElementName ) ) + { + xVBAModuleInfo->removeModuleInfo( aElementName ); + } + xVBAModuleInfo->insertModuleInfo( aElementName, aModInfo ); + } + } + + return aRetAny; +} + +SfxLibraryContainer* SfxScriptLibraryContainer::createInstanceImpl() +{ + return new SfxScriptLibraryContainer(); +} + +void SfxScriptLibraryContainer::importFromOldStorage( const OUString& aFile ) +{ + // TODO: move loading from old storage to binary filters? + auto xStorage = tools::make_ref<SotStorage>( false, aFile ); + if( xStorage->GetError() == ERRCODE_NONE ) + { + std::unique_ptr<BasicManager> pBasicManager(new BasicManager( *xStorage, aFile )); + + // Set info + LibraryContainerInfo aInfo( this, nullptr, static_cast< OldBasicPassword* >( this ) ); + pBasicManager->SetLibraryContainerInfo( aInfo ); + } +} + + +// Storing with password encryption + +// Methods XLibraryContainerPassword +sal_Bool SAL_CALL SfxScriptLibraryContainer::isLibraryPasswordProtected( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + bool bRet = pImplLib->mbPasswordProtected; + return bRet; +} + +sal_Bool SAL_CALL SfxScriptLibraryContainer::isLibraryPasswordVerified( const OUString& Name ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + if( !pImplLib->mbPasswordProtected ) + { + throw IllegalArgumentException(); + } + bool bRet = pImplLib->mbPasswordVerified; + return bRet; +} + +sal_Bool SAL_CALL SfxScriptLibraryContainer::verifyLibraryPassword + ( const OUString& Name, const OUString& Password ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + if( !pImplLib->mbPasswordProtected || pImplLib->mbPasswordVerified ) + { + throw IllegalArgumentException(); + } + // Test password + bool bSuccess = false; + if( pImplLib->mbDoc50Password ) + { + bSuccess = ( Password == pImplLib->maPassword ); + if( bSuccess ) + { + pImplLib->mbPasswordVerified = true; + } + } + else + { + pImplLib->maPassword = Password; + bSuccess = implLoadPasswordLibrary( pImplLib, Name, true ); + if( bSuccess ) + { + // The library gets modified by verifying the password, because other- + // wise for saving the storage would be copied and that doesn't work + // with mtg's storages when the password is verified + pImplLib->implSetModified( true ); + pImplLib->mbPasswordVerified = true; + + // Reload library to get source + if( pImplLib->mbLoaded ) + { + implLoadPasswordLibrary( pImplLib, Name ); + } + } + } + return bSuccess; +} + +void SAL_CALL SfxScriptLibraryContainer::changeLibraryPassword( const OUString& Name, + const OUString& OldPassword, + const OUString& NewPassword ) +{ + LibraryContainerMethodGuard aGuard( *this ); + SfxLibrary* pImplLib = getImplLib( Name ); + if( OldPassword == NewPassword ) + { + return; + } + bool bOldPassword = !OldPassword.isEmpty(); + bool bNewPassword = !NewPassword.isEmpty(); + bool bStorage = mxStorage.is() && !pImplLib->mbLink; + + if( pImplLib->mbReadOnly || (bOldPassword && !pImplLib->mbPasswordProtected) ) + { + throw IllegalArgumentException(); + } + // Library must be loaded + loadLibrary( Name ); + + bool bKillCryptedFiles = false; + bool bKillUncryptedFiles = false; + + // Remove or change password? + if( bOldPassword ) + { + if( isLibraryPasswordVerified( Name ) ) + { + if( pImplLib->maPassword != OldPassword ) + { + throw IllegalArgumentException(); + } + } + else + { + if( !verifyLibraryPassword( Name, OldPassword ) ) + { + throw IllegalArgumentException(); + } + // Reload library to get source + // Should be done in verifyLibraryPassword loadLibrary( Name ); + } + + if( !bNewPassword ) + { + pImplLib->mbPasswordProtected = false; + pImplLib->mbPasswordVerified = false; + pImplLib->maPassword.clear(); + + maModifiable.setModified( true ); + pImplLib->implSetModified( true ); + + if( !bStorage && !pImplLib->mbDoc50Password ) + { + // Store application basic unencrypted + uno::Reference< embed::XStorage > xStorage; + storeLibraries_Impl( xStorage, false ); + bKillCryptedFiles = true; + } + } + } + + // Set new password? + if( bNewPassword ) + { + pImplLib->mbPasswordProtected = true; + pImplLib->mbPasswordVerified = true; + pImplLib->maPassword = NewPassword; + SfxScriptLibrary *const pSL(dynamic_cast<SfxScriptLibrary *>(pImplLib)); + if (pSL && pSL->mbLoaded) + { + pSL->mbLoadedSource = true; // must store source code now! + } + + maModifiable.setModified( true ); + pImplLib->implSetModified( true ); + + if( !bStorage && !pImplLib->mbDoc50Password ) + { + // Store application basic crypted + uno::Reference< embed::XStorage > xStorage; + storeLibraries_Impl( xStorage, false ); + bKillUncryptedFiles = true; + } + } + + if( !(bKillCryptedFiles || bKillUncryptedFiles) ) + return; + + Sequence< OUString > aElementNames = pImplLib->getElementNames(); + sal_Int32 nNameCount = aElementNames.getLength(); + const OUString* pNames = aElementNames.getConstArray(); + OUString aLibDirPath = createAppLibraryFolder( pImplLib, Name ); + try + { + 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 ); + if( bKillUncryptedFiles ) + { + aElementInetObj.setExtension( maLibElementFileExtension ); + } + else + { + aElementInetObj.setExtension( "pba" ); + } + OUString aElementPath( aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + if( mxSFI->exists( aElementPath ) ) + { + mxSFI->kill( aElementPath ); + } + } + } + catch(const Exception& ) {} +} + + +static void setStreamKey( const uno::Reference< io::XStream >& xStream, const OUString& aPass ) +{ + uno::Reference< embed::XEncryptionProtectedSource > xEncrStream( xStream, uno::UNO_QUERY ); + if ( xEncrStream.is() ) + { + xEncrStream->setEncryptionPassword( aPass ); + } +} + + +// Impl methods +bool SfxScriptLibraryContainer::implStorePasswordLibrary( SfxLibrary* pLib, + const OUString& aName, + const uno::Reference< embed::XStorage >& xStorage, + const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) +{ + Reference< XSimpleFileAccess3 > xDummySFA; + return implStorePasswordLibrary( pLib, aName, xStorage, OUString(), xDummySFA, xHandler ); +} + +bool SfxScriptLibraryContainer::implStorePasswordLibrary( SfxLibrary* pLib, const OUString& aName, + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& aTargetURL, + const Reference< XSimpleFileAccess3 >& rToUseSFI, + const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) +{ + bool bExport = !aTargetURL.isEmpty(); + + BasicManager* pBasicMgr = getBasicManager(); + OSL_ENSURE( pBasicMgr, "SfxScriptLibraryContainer::implStorePasswordLibrary: cannot do this without a BasicManager!" ); + if ( !pBasicMgr ) + { + return false; + } + // Only need to handle the export case here, + // save/saveas etc are handled in sfxbasemodel::storeSelf & + // sfxbasemodel::impl_store + std::vector<OUString> aNames; + if ( bExport && pBasicMgr->LegacyPsswdBinaryLimitExceeded(aNames) ) + { + if ( xHandler.is() ) + { + ModuleSizeExceeded* pReq = new ModuleSizeExceeded( aNames ); + uno::Reference< task::XInteractionRequest > xReq( pReq ); + xHandler->handle( xReq ); + if ( pReq->isAbort() ) + { + throw util::VetoException(); + } + } + } + + StarBASIC* pBasicLib = pBasicMgr->GetLib( aName ); + if( !pBasicLib ) + { + return false; + } + Sequence< OUString > aElementNames = pLib->getElementNames(); + sal_Int32 nNameCount = aElementNames.getLength(); + const OUString* pNames = aElementNames.getConstArray(); + + bool bLink = pLib->mbLink; + bool bStorage = xStorage.is() && !bLink; + if( bStorage ) + { + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aElementName = pNames[ i ]; + + // Write binary image stream + SbModule* pMod = pBasicLib->FindModule( aElementName ); + if( pMod ) + { + OUString aCodeStreamName = aElementName + ".bin"; + try + { + uno::Reference< io::XStream > xCodeStream = xStorage->openStreamElement( + aCodeStreamName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + + if ( !xCodeStream.is() ) + { + throw uno::RuntimeException("null returned from openStreamElement"); + } + SvMemoryStream aMemStream; + /*sal_Bool bStore = */pMod->StoreBinaryData( aMemStream ); + + sal_Int32 const nSize = aMemStream.Tell(); + if (nSize < 0) { abort(); } + Sequence< sal_Int8 > aBinSeq( nSize ); + sal_Int8* pData = aBinSeq.getArray(); + memcpy( pData, aMemStream.GetData(), nSize ); + + Reference< XOutputStream > xOut = xCodeStream->getOutputStream(); + if ( !xOut.is() ) + { + throw io::IOException(); // access denied because the stream is readonly + } + xOut->writeBytes( aBinSeq ); + xOut->closeOutput(); + } + catch(const uno::Exception& ) + { + // TODO: handle error + } + } + + if( pLib->mbPasswordVerified || pLib->mbDoc50Password ) + { + if( !isLibraryElementValid( pLib->getByName( aElementName ) ) ) + { + SAL_WARN( "basic", "invalid library element '" << aElementName << "'."); + continue; + } + + OUString aSourceStreamName = aElementName + ".xml"; + try + { + uno::Reference< io::XStream > xSourceStream = xStorage->openStreamElement( + aSourceStreamName, + embed::ElementModes::READWRITE ); + uno::Reference< beans::XPropertySet > xProps( xSourceStream, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue("MediaType", uno::Any( OUString( "text/xml" ) ) ); + + // Set encryption key + setStreamKey( xSourceStream, pLib->maPassword ); + + Reference< XOutputStream > xOutput = xSourceStream->getOutputStream(); + Reference< XNameContainer > xLib( pLib ); + writeLibraryElement( xLib, aElementName, xOutput ); + } + catch(const uno::Exception& ) + { + OSL_FAIL( "Problem on storing of password library!" ); + // TODO: error handling + } + } + else // !mbPasswordVerified + { + // TODO + // What to do if not verified?! In any case it's already loaded here + } + } + + } + // Application libraries have only to be saved if the password + // is verified because otherwise they can't be modified + else if( pLib->mbPasswordVerified || bExport ) + { + 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 ); + } + } + else + { + aLibDirPath = createAppLibraryFolder( pLib, aName ); + } + + 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( "pba" ); + OUString aElementPath = aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( !isLibraryElementValid( pLib->getByName( aElementName ) ) ) + { + SAL_WARN( "basic", "invalid library element '" << aElementName << "'."); + continue; + } + + try + { + uno::Reference< embed::XStorage > xElementRootStorage = + ::comphelper::OStorageHelper::GetStorageFromURL( + aElementPath, + embed::ElementModes::READWRITE ); + if ( !xElementRootStorage.is() ) + { + throw uno::RuntimeException("null returned from GetStorageFromURL"); + } + // Write binary image stream + SbModule* pMod = pBasicLib->FindModule( aElementName ); + if( pMod ) + { + uno::Reference< io::XStream > xCodeStream = xElementRootStorage->openStreamElement( + "code.bin", + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ); + + SvMemoryStream aMemStream; + /*sal_Bool bStore = */pMod->StoreBinaryData( aMemStream ); + + sal_Int32 const nSize = aMemStream.Tell(); + if (nSize < 0) { abort(); } + Sequence< sal_Int8 > aBinSeq( nSize ); + sal_Int8* pData = aBinSeq.getArray(); + memcpy( pData, aMemStream.GetData(), nSize ); + + Reference< XOutputStream > xOut = xCodeStream->getOutputStream(); + if ( xOut.is() ) + { + xOut->writeBytes( aBinSeq ); + xOut->closeOutput(); + } + } + + // Write encrypted source stream + OUString aSourceStreamName( "source.xml" ); + + uno::Reference< io::XStream > xSourceStream; + try + { + xSourceStream = xElementRootStorage->openStreamElement( + aSourceStreamName, + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ); + + // #87671 Allow encryption + uno::Reference< embed::XEncryptionProtectedSource > xEncr( xSourceStream, uno::UNO_QUERY_THROW ); + xEncr->setEncryptionPassword( pLib->maPassword ); + } + catch(const css::packages::WrongPasswordException& ) + { + xSourceStream = xElementRootStorage->openEncryptedStreamElement( + aSourceStreamName, + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, + pLib->maPassword ); + } + + uno::Reference< beans::XPropertySet > xProps( xSourceStream, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue("MediaType", uno::Any( OUString( "text/xml" ) ) ); + + Reference< XOutputStream > xOut = xSourceStream->getOutputStream(); + Reference< XNameContainer > xLib( pLib ); + writeLibraryElement( xLib, aElementName, xOut ); + // i50568: sax writer already closes stream + // xOut->closeOutput(); + + uno::Reference< embed::XTransactedObject > xTransact( xElementRootStorage, uno::UNO_QUERY_THROW ); + xTransact->commit(); + } + catch(const uno::Exception& ) + { + // TODO: handle error + } + + } + } + catch(const Exception& ) + { + } + } + return true; +} + +bool SfxScriptLibraryContainer::implLoadPasswordLibrary + ( SfxLibrary* pLib, const OUString& Name, bool bVerifyPasswordOnly ) +{ + bool bRet = true; + + bool bLink = pLib->mbLink; + bool bStorage = mxStorage.is() && !bLink; + + // Already loaded? Then only verifiedPassword can change something + SfxScriptLibrary* pScriptLib = static_cast< SfxScriptLibrary* >( pLib ); + if( pScriptLib->mbLoaded ) + { + if( pScriptLib->mbLoadedBinary && !bVerifyPasswordOnly && + (pScriptLib->mbLoadedSource || !pLib->mbPasswordVerified) ) + { + return false; + } + } + + StarBASIC* pBasicLib = nullptr; + bool bLoadBinary = false; + if( !pScriptLib->mbLoadedBinary && !bVerifyPasswordOnly && !pLib->mbPasswordVerified ) + { + BasicManager* pBasicMgr = getBasicManager(); + OSL_ENSURE( pBasicMgr, "SfxScriptLibraryContainer::implLoadPasswordLibrary: cannot do this without a BasicManager!" ); + bool bLoaded = pScriptLib->mbLoaded; + pScriptLib->mbLoaded = true; // Necessary to get lib + pBasicLib = pBasicMgr ? pBasicMgr->GetLib( Name ) : nullptr; + pScriptLib->mbLoaded = bLoaded; // Restore flag + if( !pBasicLib ) + { + return false; + } + bLoadBinary = true; + pScriptLib->mbLoadedBinary = true; + } + + bool bLoadSource = false; + if( !pScriptLib->mbLoadedSource && pLib->mbPasswordVerified && !bVerifyPasswordOnly ) + { + bLoadSource = true; + pScriptLib->mbLoadedSource = true; + } + + Sequence< OUString > aElementNames = pLib->getElementNames(); + sal_Int32 nNameCount = aElementNames.getLength(); + const OUString* pNames = aElementNames.getConstArray(); + + if( bStorage ) + { + uno::Reference< embed::XStorage > xLibrariesStor; + uno::Reference< embed::XStorage > xLibraryStor; + try { + xLibrariesStor = mxStorage->openStorageElement( maLibrariesDir, embed::ElementModes::READ ); + if ( !xLibrariesStor.is() ) + { + throw uno::RuntimeException("null returned from openStorageElement"); + } + xLibraryStor = xLibrariesStor->openStorageElement( Name, embed::ElementModes::READ ); + if ( !xLibraryStor.is() ) + { + throw uno::RuntimeException("null returned from openStorageElement"); + } + } + catch(const uno::Exception& ) + { + OSL_FAIL( "### couldn't open sub storage for library" ); + return false; + } + + for( sal_Int32 i = 0 ; i < nNameCount ; i++ ) + { + OUString aElementName = pNames[ i ]; + + // Load binary + if( bLoadBinary ) + { + SbModule* pMod = pBasicLib->FindModule( aElementName ); + if( !pMod ) + { + pMod = pBasicLib->MakeModule( aElementName, OUString() ); + pBasicLib->SetModified( false ); + } + + OUString aCodeStreamName= aElementName + ".bin"; + try + { + uno::Reference< io::XStream > xCodeStream = xLibraryStor->openStreamElement( + aCodeStreamName, + embed::ElementModes::READ ); + if ( !xCodeStream.is() ) + { + throw uno::RuntimeException("null returned from openStreamElement"); + } + std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( xCodeStream )); + if ( !pStream || pStream->GetError() ) + { + sal_uInt32 nError = sal_uInt32(pStream ? pStream->GetError() : ERRCODE_IO_GENERAL); + throw task::ErrorCodeIOException( + ("utl::UcbStreamHelper::CreateStream failed for \"" + + aCodeStreamName + "\": 0x" + + OUString::number(nError, 16)), + uno::Reference< uno::XInterface >(), nError); + } + + /*sal_Bool bRet = */pMod->LoadBinaryData( *pStream ); + // TODO: Check return value + } + catch(const uno::Exception& ) + { + // TODO: error handling + } + } + + // Load source + if( bLoadSource || bVerifyPasswordOnly ) + { + // Access encrypted source stream + OUString aSourceStreamName = aElementName + ".xml"; + try + { + uno::Reference< io::XStream > xSourceStream = xLibraryStor->openEncryptedStreamElement( + aSourceStreamName, + embed::ElementModes::READ, + pLib->maPassword ); + if ( !xSourceStream.is() ) + { + throw uno::RuntimeException("null returned from openEncryptedStreamElement"); + } + // if this point is reached then the password is correct + if ( !bVerifyPasswordOnly ) + { + uno::Reference< io::XInputStream > xInStream = xSourceStream->getInputStream(); + if ( !xInStream.is() ) + { + throw io::IOException(); // read access denied, seems to be impossible + } + Reference< XNameContainer > xLib( pLib ); + Any aAny = importLibraryElement( xLib, + aElementName, aSourceStreamName, + xInStream ); + if( pLib->hasByName( aElementName ) ) + { + if( aAny.hasValue() ) + { + pLib->maNameContainer->replaceByName( aElementName, aAny ); + } + } + else + { + pLib->maNameContainer->insertByName( aElementName, aAny ); + } + } + } + catch(const uno::Exception& ) + { + bRet = false; + } + } + } + } + else + { + try + { + OUString aLibDirPath = createAppLibraryFolder( pLib, Name ); + + 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( "pba" ); + OUString aElementPath = aElementInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + uno::Reference< embed::XStorage > xElementRootStorage; + try + { + xElementRootStorage = ::comphelper::OStorageHelper::GetStorageFromURL( + aElementPath, + embed::ElementModes::READ ); + } catch(const uno::Exception& ) + { + // TODO: error handling + } + + if ( xElementRootStorage.is() ) + { + // Load binary + if( bLoadBinary ) + { + SbModule* pMod = pBasicLib->FindModule( aElementName ); + if( !pMod ) + { + pMod = pBasicLib->MakeModule( aElementName, OUString() ); + pBasicLib->SetModified( false ); + } + + try + { + uno::Reference< io::XStream > xCodeStream = xElementRootStorage->openStreamElement( + "code.bin", + embed::ElementModes::READ ); + + std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( xCodeStream )); + if ( !pStream || pStream->GetError() ) + { + sal_uInt32 nError = sal_uInt32(pStream ? pStream->GetError() : ERRCODE_IO_GENERAL); + throw task::ErrorCodeIOException( + ("utl::UcbStreamHelper::CreateStream failed" + " for code.bin: 0x" + + OUString::number(nError, 16)), + uno::Reference< uno::XInterface >(), + nError); + } + + /*sal_Bool bRet = */pMod->LoadBinaryData( *pStream ); + // TODO: Check return value + } + catch(const uno::Exception& ) + { + // TODO: error handling + } + } + + // Load source + if( bLoadSource || bVerifyPasswordOnly ) + { + // Access encrypted source stream + OUString aSourceStreamName( "source.xml" ); + try + { + uno::Reference< io::XStream > xSourceStream = xElementRootStorage->openEncryptedStreamElement( + aSourceStreamName, + embed::ElementModes::READ, + pLib->maPassword ); + if ( !xSourceStream.is() ) + { + throw uno::RuntimeException("null returned from openEncryptedStreamElement"); + } + if ( !bVerifyPasswordOnly ) + { + uno::Reference< io::XInputStream > xInStream = xSourceStream->getInputStream(); + if ( !xInStream.is() ) + { + throw io::IOException(); // read access denied, seems to be impossible + } + Reference< XNameContainer > xLib( pLib ); + Any aAny = importLibraryElement( xLib, + aElementName, + aSourceStreamName, + xInStream ); + if( pLib->hasByName( aElementName ) ) + { + if( aAny.hasValue() ) + { + pLib->maNameContainer->replaceByName( aElementName, aAny ); + } + } + else + { + pLib->maNameContainer->insertByName( aElementName, aAny ); + } + } + } + catch (const uno::Exception& ) + { + bRet = false; + } + } + } + } + } + catch(const Exception& ) + { + // TODO + //throw e; + } + } + + return bRet; +} + + +void SfxScriptLibraryContainer::onNewRootStorage() +{ +} + +sal_Bool SAL_CALL SfxScriptLibraryContainer:: HasExecutableCode( const OUString& Library ) +{ + BasicManager* pBasicMgr = getBasicManager(); + OSL_ENSURE( pBasicMgr, "we need a basicmanager, really we do" ); + if ( pBasicMgr ) + { + return pBasicMgr->HasExeCode( Library ); // need to change this to take name + } + // default to it has code if we can't decide + return true; +} + + +// Service +OUString SAL_CALL SfxScriptLibraryContainer::getImplementationName( ) +{ + return "com.sun.star.comp.sfx2.ScriptLibraryContainer"; +} + +Sequence< OUString > SAL_CALL SfxScriptLibraryContainer::getSupportedServiceNames( ) +{ + return {"com.sun.star.script.DocumentScriptLibraryContainer", + "com.sun.star.script.ScriptLibraryContainer"}; // for compatibility +} + +// Implementation class SfxScriptLibrary + +// Ctor +SfxScriptLibrary::SfxScriptLibrary( ModifiableHelper& _rModifiable, + const Reference< XSimpleFileAccess3 >& xSFI ) + : SfxLibrary( _rModifiable, cppu::UnoType<OUString>::get(), xSFI ) + , mbLoadedSource( false ) + , mbLoadedBinary( false ) +{ +} + +SfxScriptLibrary::SfxScriptLibrary( ModifiableHelper& _rModifiable, + const Reference< XSimpleFileAccess3 >& xSFI, + const OUString& aLibInfoFileURL, + const OUString& aStorageURL, + bool ReadOnly ) + : SfxLibrary( _rModifiable, cppu::UnoType<OUString>::get(), xSFI, + aLibInfoFileURL, aStorageURL, ReadOnly) + , mbLoadedSource( false ) + , mbLoadedBinary( false ) +{ +} + +bool SfxScriptLibrary::isLoadedStorable() +{ + // note: mbLoadedSource can only be true for password-protected lib! + return SfxLibrary::isLoadedStorable() && (!mbPasswordProtected || mbLoadedSource); +} + +// Provide modify state including resources +bool SfxScriptLibrary::isModified() +{ + return implIsModified(); // No resources +} + +void SfxScriptLibrary::storeResources() +{ + // No resources +} + +void SfxScriptLibrary::storeResourcesToURL( const OUString&, + const Reference< task::XInteractionHandler >& ) +{} + +void SfxScriptLibrary::storeResourcesAsURL + ( const OUString&, const OUString& ) +{} + +void SfxScriptLibrary::storeResourcesToStorage( const css::uno::Reference + < css::embed::XStorage >& ) +{ + // No resources +} + +bool SfxScriptLibrary::containsValidModule(const Any& rElement) +{ + OUString sModuleText; + rElement >>= sModuleText; + return ( !sModuleText.isEmpty() ); +} + +bool SfxScriptLibrary::isLibraryElementValid(const css::uno::Any& rElement) const +{ + return SfxScriptLibrary::containsValidModule(rElement); +} + +IMPLEMENT_FORWARD_XINTERFACE2( SfxScriptLibrary, SfxLibrary, SfxScriptLibrary_BASE ); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( SfxScriptLibrary, SfxLibrary, SfxScriptLibrary_BASE ); + +script::ModuleInfo SAL_CALL SfxScriptLibrary::getModuleInfo( const OUString& ModuleName ) +{ + if ( !hasModuleInfo( ModuleName ) ) + { + throw NoSuchElementException(); + } + return mModuleInfo[ ModuleName ]; +} + +sal_Bool SAL_CALL SfxScriptLibrary::hasModuleInfo( const OUString& ModuleName ) +{ + bool bRes = false; + ModuleInfoMap::iterator it = mModuleInfo.find( ModuleName ); + + if ( it != mModuleInfo.end() ) + { + bRes = true; + } + return bRes; +} + +void SAL_CALL SfxScriptLibrary::insertModuleInfo( const OUString& ModuleName, const script::ModuleInfo& ModuleInfo ) +{ + if ( hasModuleInfo( ModuleName ) ) + { + throw ElementExistException(); + } + mModuleInfo[ ModuleName ] = ModuleInfo; +} + +void SAL_CALL SfxScriptLibrary::removeModuleInfo( const OUString& ModuleName ) +{ + // #FIXME add NoSuchElementException to the spec + if ( mModuleInfo.erase( ModuleName ) == 0 ) + throw NoSuchElementException(); +} + +} // namespace basic + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_sfx2_ScriptLibraryContainer_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new basic::SfxScriptLibraryContainer()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |