diff options
Diffstat (limited to 'desktop/source/migration')
-rw-r--r-- | desktop/source/migration/migration.cxx | 1111 | ||||
-rw-r--r-- | desktop/source/migration/migration_impl.hxx | 198 | ||||
-rw-r--r-- | desktop/source/migration/services/basicmigration.cxx | 202 | ||||
-rw-r--r-- | desktop/source/migration/services/basicmigration.hxx | 71 | ||||
-rw-r--r-- | desktop/source/migration/services/cppumaker.mk | 27 | ||||
-rw-r--r-- | desktop/source/migration/services/jvmfwk.cxx | 395 | ||||
-rw-r--r-- | desktop/source/migration/services/jvmfwk.hxx | 33 | ||||
-rw-r--r-- | desktop/source/migration/services/migrationoo2.component | 30 | ||||
-rw-r--r-- | desktop/source/migration/services/migrationoo3.component | 26 | ||||
-rw-r--r-- | desktop/source/migration/services/misc.hxx | 39 | ||||
-rw-r--r-- | desktop/source/migration/services/oo3extensionmigration.cxx | 409 | ||||
-rw-r--r-- | desktop/source/migration/services/oo3extensionmigration.hxx | 117 | ||||
-rw-r--r-- | desktop/source/migration/services/wordbookmigration.cxx | 231 | ||||
-rw-r--r-- | desktop/source/migration/services/wordbookmigration.hxx | 72 |
14 files changed, 2961 insertions, 0 deletions
diff --git a/desktop/source/migration/migration.cxx b/desktop/source/migration/migration.cxx new file mode 100644 index 000000000..524ca8615 --- /dev/null +++ b/desktop/source/migration/migration.cxx @@ -0,0 +1,1111 @@ +/* -*- 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 <sal/config.h> + +#include <algorithm> +#include <iterator> +#include <map> +#include <set> + +#include <migration.hxx> +#include "migration_impl.hxx" + +#include <sal/log.hxx> +#include <unotools/textsearch.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <unotools/bootstrap.hxx> +#include <rtl/uri.hxx> +#include <i18nlangtag/lang.h> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <osl/file.hxx> +#include <osl/security.hxx> +#include <unotools/configmgr.hxx> + +#include <com/sun/star/configuration/Update.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/FileSystemStorageFactory.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/UIConfigurationManager.hpp> +#include <com/sun/star/ui/XUIConfigurationPersistence.hpp> +#include <vcl/commandinfoprovider.hxx> + +using namespace osl; +using namespace com::sun::star::task; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::container; +using com::sun::star::uno::Exception; +using namespace com::sun::star; + + +namespace desktop +{ + +constexpr OUStringLiteral ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"; +constexpr OUStringLiteral ITEM_DESCRIPTOR_CONTAINER = u"ItemDescriptorContainer"; +constexpr OUStringLiteral ITEM_DESCRIPTOR_LABEL = u"Label"; + +static OUString mapModuleShortNameToIdentifier(std::u16string_view sShortName) +{ + OUString sIdentifier; + + if ( sShortName == u"StartModule" ) + sIdentifier = "com.sun.star.frame.StartModule"; + + else if ( sShortName == u"swriter" ) + sIdentifier = "com.sun.star.text.TextDocument"; + + else if ( sShortName == u"scalc" ) + sIdentifier = "com.sun.star.sheet.SpreadsheetDocument"; + + else if ( sShortName == u"sdraw" ) + sIdentifier = "com.sun.star.drawing.DrawingDocument"; + + else if ( sShortName == u"simpress" ) + sIdentifier = "com.sun.star.presentation.PresentationDocument"; + + else if ( sShortName == u"smath" ) + sIdentifier = "com.sun.star.formula.FormulaProperties"; + + else if ( sShortName == u"schart" ) + sIdentifier = "com.sun.star.chart2.ChartDocument"; + + else if ( sShortName == u"BasicIDE" ) + sIdentifier = "com.sun.star.script.BasicIDE"; + + else if ( sShortName == u"dbapp" ) + sIdentifier = "com.sun.star.sdb.OfficeDatabaseDocument"; + + else if ( sShortName == u"sglobal" ) + sIdentifier = "com.sun.star.text.GlobalDocument"; + + else if ( sShortName == u"sweb" ) + sIdentifier = "com.sun.star.text.WebDocument"; + + else if ( sShortName == u"swxform" ) + sIdentifier = "com.sun.star.xforms.XMLFormDocument"; + + else if ( sShortName == u"sbibliography" ) + sIdentifier = "com.sun.star.frame.Bibliography"; + + return sIdentifier; +} + +bool MigrationImpl::alreadyMigrated() +{ + OUString aStr = m_aInfo.userdata + "/MIGRATED4"; + File aFile(aStr); + // create migration stamp, and/or check its existence + bool bRet = aFile.open (osl_File_OpenFlag_Write | osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock) == FileBase::E_EXIST; + SAL_INFO( "desktop.migration", "File '" << aStr << "' exists? " << bRet ); + return bRet; +} + +bool MigrationImpl::initializeMigration() +{ + bool bRet = false; + + if (!checkMigrationCompleted()) { + readAvailableMigrations(m_vMigrationsAvailable); + sal_Int32 nIndex = findPreferredMigrationProcess(m_vMigrationsAvailable); + // m_aInfo is now set to the preferred migration source + if ( nIndex >= 0 ) { + if (alreadyMigrated()) + return false; + m_vrMigrations = readMigrationSteps(m_vMigrationsAvailable[nIndex].name); + } + + bRet = !m_aInfo.userdata.isEmpty(); + } + + SAL_INFO( "desktop.migration", "Migration " << ( bRet ? "needed" : "not required" ) ); + + return bRet; +} + +void Migration::migrateSettingsIfNecessary() +{ + MigrationImpl aImpl; + + if (! aImpl.initializeMigration() ) + return; + + bool bResult = false; + try { + bResult = aImpl.doMigration(); + } catch (const Exception&) { + TOOLS_WARN_EXCEPTION( "desktop", "doMigration()"); + } + OSL_ENSURE(bResult, "Migration has not been successful"); +} + +MigrationImpl::MigrationImpl() +{ +} + +MigrationImpl::~MigrationImpl() +{ +} + +// The main entry point for migrating settings +bool MigrationImpl::doMigration() +{ + // compile file list for migration + m_vrFileList = compileFileList(); + + bool result = false; + try { + NewVersionUIInfo aNewVersionUIInfo; + std::vector< MigrationModuleInfo > vModulesInfo = detectUIChangesForAllModules(); + aNewVersionUIInfo.init(vModulesInfo); + + copyFiles(); + + static const OUStringLiteral sMenubarResourceURL(u"private:resource/menubar/menubar"); + static const OUStringLiteral sToolbarResourcePre(u"private:resource/toolbar/"); + for (MigrationModuleInfo & i : vModulesInfo) { + OUString sModuleIdentifier = mapModuleShortNameToIdentifier(i.sModuleShortName); + if (sModuleIdentifier.isEmpty()) + continue; + + + OUString aOldCfgDataPath = m_aInfo.userdata + "/user/config/soffice.cfg/modules/" + i.sModuleShortName; + uno::Sequence< uno::Any > lArgs {uno::Any(aOldCfgDataPath), uno::Any(embed::ElementModes::READ)}; + + uno::Reference< uno::XComponentContext > xContext(comphelper::getProcessComponentContext()); + uno::Reference< lang::XSingleServiceFactory > xStorageFactory(embed::FileSystemStorageFactory::create(xContext)); + uno::Reference< embed::XStorage > xModules(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY); + uno::Reference< ui::XUIConfigurationManager2 > xOldCfgManager = ui::UIConfigurationManager::create(xContext); + + if ( xModules.is() ) { + xOldCfgManager->setStorage( xModules ); + xOldCfgManager->reload(); + } + + uno::Reference< ui::XUIConfigurationManager > xCfgManager = aNewVersionUIInfo.getConfigManager(i.sModuleShortName); + + if (i.bHasMenubar) { + uno::Reference< container::XIndexContainer > xOldVersionMenuSettings(xOldCfgManager->getSettings(sMenubarResourceURL, true), uno::UNO_QUERY); + uno::Reference< container::XIndexContainer > xNewVersionMenuSettings = aNewVersionUIInfo.getNewMenubarSettings(i.sModuleShortName); + compareOldAndNewConfig(OUString(), xOldVersionMenuSettings, xNewVersionMenuSettings, sMenubarResourceURL); + mergeOldToNewVersion(xCfgManager, xNewVersionMenuSettings, sModuleIdentifier, sMenubarResourceURL); + } + + sal_Int32 nToolbars = i.m_vToolbars.size(); + if (nToolbars >0) { + for (sal_Int32 j=0; j<nToolbars; ++j) { + OUString sToolbarName = i.m_vToolbars[j]; + OUString sToolbarResourceURL = sToolbarResourcePre + sToolbarName; + + uno::Reference< container::XIndexContainer > xOldVersionToolbarSettings(xOldCfgManager->getSettings(sToolbarResourceURL, true), uno::UNO_QUERY); + uno::Reference< container::XIndexContainer > xNewVersionToolbarSettings = aNewVersionUIInfo.getNewToolbarSettings(i.sModuleShortName, sToolbarName); + compareOldAndNewConfig(OUString(), xOldVersionToolbarSettings, xNewVersionToolbarSettings, sToolbarResourceURL); + mergeOldToNewVersion(xCfgManager, xNewVersionToolbarSettings, sModuleIdentifier, sToolbarResourceURL); + } + } + + m_aOldVersionItemsHashMap.clear(); + } + + // execute the migration items from Setup.xcu + copyConfig(); + + // execute custom migration services from Setup.xcu + // and refresh the cache + runServices(); + uno::Reference< XRefreshable >( + configuration::theDefaultProvider::get(comphelper::getProcessComponentContext()), + uno::UNO_QUERY_THROW)->refresh(); + + result = true; + } catch (const css::uno::Exception &) { + TOOLS_WARN_EXCEPTION( + "desktop.migration", + "ignored Exception while migrating from version \"" << m_aInfo.productname + << "\" data \"" << m_aInfo.userdata << "\""); + } + + // prevent running the migration multiple times + setMigrationCompleted(); + return result; +} + +void MigrationImpl::setMigrationCompleted() +{ + try { + uno::Reference< XPropertySet > aPropertySet(getConfigAccess("org.openoffice.Setup/Office", true), uno::UNO_QUERY_THROW); + aPropertySet->setPropertyValue("MigrationCompleted", uno::Any(true)); + uno::Reference< XChangesBatch >(aPropertySet, uno::UNO_QUERY_THROW)->commitChanges(); + } catch (...) { + // fail silently + } +} + +bool MigrationImpl::checkMigrationCompleted() +{ + bool bMigrationCompleted = false; + try { + uno::Reference< XPropertySet > aPropertySet( + getConfigAccess("org.openoffice.Setup/Office"), uno::UNO_QUERY_THROW); + aPropertySet->getPropertyValue("MigrationCompleted") >>= bMigrationCompleted; + + if( !bMigrationCompleted && getenv("SAL_DISABLE_USERMIGRATION" ) ) { + // migration prevented - fake its success + setMigrationCompleted(); + bMigrationCompleted = true; + } + } catch (const Exception&) { + // just return false... + } + SAL_INFO( "desktop.migration", "Migration " << ( bMigrationCompleted ? "already completed" : "not done" ) ); + + return bMigrationCompleted; +} + +static void insertSorted(migrations_available& rAvailableMigrations, supported_migration const & aSupportedMigration) +{ + migrations_available::iterator pIter = std::find_if(rAvailableMigrations.begin(), rAvailableMigrations.end(), + [&aSupportedMigration](const supported_migration& rMigration) { return rMigration.nPriority < aSupportedMigration.nPriority; }); + if (pIter != rAvailableMigrations.end()) + rAvailableMigrations.insert(pIter, aSupportedMigration ); + else + rAvailableMigrations.push_back( aSupportedMigration ); +} + +void MigrationImpl::readAvailableMigrations(migrations_available& rAvailableMigrations) +{ + // get supported version names + uno::Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration/SupportedVersions"), uno::UNO_SET_THROW); + const uno::Sequence< OUString > seqSupportedVersions = aMigrationAccess->getElementNames(); + + static const OUStringLiteral aVersionIdentifiers( u"VersionIdentifiers" ); + static const OUStringLiteral aPriorityIdentifier( u"Priority" ); + + for (OUString const & supportedVersion :seqSupportedVersions) { + sal_Int32 nPriority( 0 ); + uno::Sequence< OUString > seqVersions; + uno::Reference< XNameAccess > xMigrationData( aMigrationAccess->getByName(supportedVersion), uno::UNO_QUERY_THROW ); + xMigrationData->getByName( aVersionIdentifiers ) >>= seqVersions; + xMigrationData->getByName( aPriorityIdentifier ) >>= nPriority; + + supported_migration aSupportedMigration; + aSupportedMigration.name = supportedVersion; + aSupportedMigration.nPriority = nPriority; + for (OUString const & s : std::as_const(seqVersions)) + aSupportedMigration.supported_versions.push_back(s.trim()); + insertSorted( rAvailableMigrations, aSupportedMigration ); + SAL_INFO( "desktop.migration", " available migration '" << aSupportedMigration.name << "'" ); + } +} + +migrations_vr MigrationImpl::readMigrationSteps(const OUString& rMigrationName) +{ + // get migration access + uno::Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration/SupportedVersions"), uno::UNO_SET_THROW); + uno::Reference< XNameAccess > xMigrationData( aMigrationAccess->getByName(rMigrationName), uno::UNO_QUERY_THROW ); + + // get migration description from org.openoffice.Setup/Migration + // and build vector of migration steps + uno::Reference< XNameAccess > theNameAccess(xMigrationData->getByName("MigrationSteps"), uno::UNO_QUERY_THROW); + uno::Reference< XNameAccess > tmpAccess; + uno::Sequence< OUString > tmpSeq; + migrations_vr vrMigrations(new migrations_v); + const css::uno::Sequence<OUString> aMigrationSteps = theNameAccess->getElementNames(); + for (const OUString& rMigrationStep : aMigrationSteps) { + // get current migration step + theNameAccess->getByName(rMigrationStep) >>= tmpAccess; + migration_step tmpStep; + + // read included files from current step description + if (tmpAccess->getByName("IncludedFiles") >>= tmpSeq) { + for (const OUString& rSeqEntry : std::as_const(tmpSeq)) + tmpStep.includeFiles.push_back(rSeqEntry); + } + + // excluded files... + if (tmpAccess->getByName("ExcludedFiles") >>= tmpSeq) { + for (const OUString& rSeqEntry : std::as_const(tmpSeq)) + tmpStep.excludeFiles.push_back(rSeqEntry); + } + + // included nodes... + if (tmpAccess->getByName("IncludedNodes") >>= tmpSeq) { + for (const OUString& rSeqEntry : std::as_const(tmpSeq)) + tmpStep.includeConfig.push_back(rSeqEntry); + } + + // excluded nodes... + if (tmpAccess->getByName("ExcludedNodes") >>= tmpSeq) { + for (const OUString& rSeqEntry : std::as_const(tmpSeq)) + tmpStep.excludeConfig.push_back(rSeqEntry); + } + + // excluded extensions... + if (tmpAccess->getByName("ExcludedExtensions") >>= tmpSeq) { + for (const OUString& rSeqEntry : std::as_const(tmpSeq)) + tmpStep.excludeExtensions.push_back(rSeqEntry); + } + + // generic service + tmpAccess->getByName("MigrationService") >>= tmpStep.service; + + vrMigrations->push_back(tmpStep); + } + return vrMigrations; +} + +static FileBase::RC _checkAndCreateDirectory(INetURLObject const & dirURL) +{ + FileBase::RC result = Directory::create(dirURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)); + if (result == FileBase::E_NOENT) { + INetURLObject baseURL(dirURL); + baseURL.removeSegment(); + _checkAndCreateDirectory(baseURL); + return Directory::create(dirURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)); + } else + return result; +} + +#if defined UNX && ! defined MACOSX + +const char XDG_CONFIG_PART[] = "/.config/"; + +OUString MigrationImpl::preXDGConfigDir(const OUString& rConfigDir) +{ + OUString aPreXDGConfigPath; + const char* pXDGCfgHome = getenv("XDG_CONFIG_HOME"); + + // cater for XDG_CONFIG_HOME change + // If XDG_CONFIG_HOME is set then we; + // assume the user knows what they are doing ( room for improvement here, we could + // of course search the default config dir etc. also - but this is more complex, + // we would need to weigh results from the current config dir against matches in + // the 'old' config dir etc. ) - currently we just use the returned config dir. + // If XDG_CONFIG_HOME is NOT set; + // assume then we should now using the default $HOME/.config config location for + // our user profiles, however *all* previous libreoffice and openoffice.org + // configurations will be in the 'old' config directory and that's where we need + // to search - we convert the returned config dir to the 'old' dir + if ( !pXDGCfgHome && rConfigDir.endsWith( XDG_CONFIG_PART ) ) + // remove trailing '.config/' but leave the terminating '/' + aPreXDGConfigPath = rConfigDir.copy( 0, rConfigDir.getLength() - sizeof( XDG_CONFIG_PART ) + 2 ); + else + aPreXDGConfigPath = rConfigDir; + + // the application-specific config dir is no longer prefixed by '.' because it is hidden under ".config" + // we have to add the '.' for the pre-XDG directory names + aPreXDGConfigPath += "."; + + return aPreXDGConfigPath; +} +#endif + +void MigrationImpl::setInstallInfoIfExist( + install_info& aInfo, + std::u16string_view rConfigDir, + const OUString& rVersion) +{ + OUString url(INetURLObject(rConfigDir).GetMainURL(INetURLObject::DecodeMechanism::NONE)); + osl::DirectoryItem item; + osl::FileStatus stat(osl_FileStatus_Mask_Type); + + if (osl::DirectoryItem::get(url, item) == osl::FileBase::E_None + && item.getFileStatus(stat) == osl::FileBase::E_None + && stat.getFileType() == osl::FileStatus::Directory) { + aInfo.userdata = url; + aInfo.productname = rVersion; + } +} + +install_info MigrationImpl::findInstallation(const strings_v& rVersions) +{ + + OUString aTopConfigDir; + osl::Security().getConfigDir( aTopConfigDir ); + if ( !aTopConfigDir.isEmpty() && aTopConfigDir[ aTopConfigDir.getLength()-1 ] != '/' ) + aTopConfigDir += "/"; + +#if defined UNX && ! defined MACOSX + OUString aPreXDGTopConfigDir = preXDGConfigDir(aTopConfigDir); +#endif + + install_info aInfo; + for (auto const& elem : rVersions) + { + OUString aVersion, aProfileName; + sal_Int32 nSeparatorIndex = elem.indexOf('='); + if ( nSeparatorIndex != -1 ) { + aVersion = elem.copy( 0, nSeparatorIndex ); + aProfileName = elem.copy( nSeparatorIndex+1 ); + } + + if ( !aVersion.isEmpty() && !aProfileName.isEmpty() && + ( aInfo.userdata.isEmpty() || + aProfileName.equalsIgnoreAsciiCase( + utl::ConfigManager::getProductName() ) ) ) { + setInstallInfoIfExist(aInfo, OUStringConcatenation(aTopConfigDir + aProfileName), aVersion); +#if defined UNX && ! defined MACOSX + //try preXDG path if the new one does not exist + if ( aInfo.userdata.isEmpty()) + setInstallInfoIfExist(aInfo, OUStringConcatenation(aPreXDGTopConfigDir + aProfileName), aVersion); +#endif + } + } + + return aInfo; +} + +sal_Int32 MigrationImpl::findPreferredMigrationProcess(const migrations_available& rAvailableMigrations) +{ + sal_Int32 nIndex( -1 ); + sal_Int32 i( 0 ); + + for (auto const& availableMigration : rAvailableMigrations) + { + install_info aInstallInfo = findInstallation(availableMigration.supported_versions); + if (!aInstallInfo.productname.isEmpty() ) { + m_aInfo = aInstallInfo; + nIndex = i; + break; + } + ++i; + } + + SAL_INFO( "desktop.migration", " preferred migration is from product '" << m_aInfo.productname << "'"); + SAL_INFO( "desktop.migration", " and settings directory '" << m_aInfo.userdata << "'"); + + return nIndex; +} + +strings_vr MigrationImpl::applyPatterns(const strings_v& vSet, const strings_v& vPatterns) +{ + using namespace utl; + strings_vr vrResult(new strings_v); + for (auto const& pattern : vPatterns) + { + // find matches for this pattern in input set + // and copy them to the result + SearchParam param(pattern, SearchParam::SearchType::Regexp); + TextSearch ts(param, LANGUAGE_DONTKNOW); + sal_Int32 start = 0; + sal_Int32 end = 0; + for (auto const& elem : vSet) + { + end = elem.getLength(); + if (ts.SearchForward(elem, &start, &end)) + vrResult->push_back(elem); + } + } + return vrResult; +} + +strings_vr MigrationImpl::getAllFiles(const OUString& baseURL) const +{ + strings_vr vrResult(new strings_v); + + // get sub dirs + Directory dir(baseURL); + if (dir.open() == FileBase::E_None) { + strings_v vSubDirs; + strings_vr vrSubResult; + + // work through directory contents... + DirectoryItem item; + FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL); + while (dir.getNextItem(item) == FileBase::E_None) { + if (item.getFileStatus(fs) == FileBase::E_None) { + if (fs.getFileType() == FileStatus::Directory) + vSubDirs.push_back(fs.getFileURL()); + else + vrResult->push_back(fs.getFileURL()); + } + } + + // recurse subfolders + for (auto const& subDir : vSubDirs) + { + vrSubResult = getAllFiles(subDir); + vrResult->insert(vrResult->end(), vrSubResult->begin(), vrSubResult->end()); + } + } + return vrResult; +} + +namespace +{ + +// removes elements of vector 2 in vector 1 +strings_v subtract(strings_v && a, strings_v && b) +{ + std::sort(a.begin(), a.end()); + strings_v::iterator ae(std::unique(a.begin(), a.end())); + std::sort(b.begin(), b.end()); + strings_v::iterator be(std::unique(b.begin(), b.end())); + strings_v c; + std::set_difference(a.begin(), ae, b.begin(), be, std::back_inserter(c)); + return c; +} + +} + +strings_vr MigrationImpl::compileFileList() +{ + + strings_vr vrResult(new strings_v); + + // get a list of all files: + strings_vr vrFiles = getAllFiles(m_aInfo.userdata); + + // get a file list result for each migration step + for (auto const& rMigration : *m_vrMigrations) + { + strings_vr vrInclude = applyPatterns(*vrFiles, rMigration.includeFiles); + strings_vr vrExclude = applyPatterns(*vrFiles, rMigration.excludeFiles); + strings_v sub(subtract(std::move(*vrInclude), std::move(*vrExclude))); + vrResult->insert(vrResult->end(), sub.begin(), sub.end()); + } + return vrResult; +} + +namespace +{ + +struct componentParts { + std::set< OUString > includedPaths; + std::set< OUString > excludedPaths; +}; + +typedef std::map< OUString, componentParts > Components; + +bool getComponent(OUString const & path, OUString * component) +{ + OSL_ASSERT(component != nullptr); + if (path.isEmpty() || path[0] != '/') { + SAL_INFO( "desktop.migration", "configuration migration in/exclude path " << path << " ignored (does not start with slash)" ); + return false; + } + sal_Int32 i = path.indexOf('/', 1); + *component = i < 0 ? path.copy(1) : path.copy(1, i - 1); + return true; +} + +} + +void MigrationImpl::copyConfig() +{ + Components comps; + for (auto const& rMigrationStep : *m_vrMigrations) { + for (const OUString& rIncludePath : rMigrationStep.includeConfig) { + OUString comp; + if (getComponent(rIncludePath, &comp)) { + comps[comp].includedPaths.insert(rIncludePath); + } + } + for (const OUString& rExcludePath : rMigrationStep.excludeConfig) { + OUString comp; + if (getComponent(rExcludePath, &comp)) { + comps[comp].excludedPaths.insert(rExcludePath); + } + } + } + + // check if the shared registrymodifications.xcu file exists + bool bRegistryModificationsXcuExists = false; + OUString regFilePath = m_aInfo.userdata + "/user/registrymodifications.xcu"; + File regFile(regFilePath); + ::osl::FileBase::RC nError = regFile.open(osl_File_OpenFlag_Read); + if ( nError == ::osl::FileBase::E_None ) { + bRegistryModificationsXcuExists = true; + regFile.close(); + } + + for (auto const& comp : comps) + { + if (!comp.second.includedPaths.empty()) { + if (!bRegistryModificationsXcuExists) { + // shared registrymodifications.xcu does not exists + // the configuration is split in many registry files + // determine the file names from the first element in included paths + OUStringBuffer buf(m_aInfo.userdata); + buf.append("/user/registry/data"); + sal_Int32 n = 0; + do { + OUString seg(comp.first.getToken(0, '.', n)); + OUString enc( + rtl::Uri::encode( + seg, rtl_UriCharClassPchar, rtl_UriEncodeStrict, + RTL_TEXTENCODING_UTF8)); + if (enc.isEmpty() && !seg.isEmpty()) { + SAL_INFO( "desktop.migration", "configuration migration component " << comp.first << " ignored (cannot be encoded as file path)" ); + goto next; + } + buf.append('/'); + buf.append(enc); + } while (n >= 0); + buf.append(".xcu"); + regFilePath = buf.makeStringAndClear(); + } + configuration::Update::get( + comphelper::getProcessComponentContext())-> + insertModificationXcuFile( + regFilePath, + comphelper::containerToSequence(comp.second.includedPaths), + comphelper::containerToSequence(comp.second.excludedPaths)); + } else { + SAL_INFO( "desktop.migration", "configuration migration component " << comp.first << " ignored (only excludes, no includes)" ); + } +next: + ; + } +} + +uno::Reference< XNameAccess > MigrationImpl::getConfigAccess(const char* pPath, bool bUpdate) +{ + uno::Reference< XNameAccess > xNameAccess; + try { + OUString sAccessSrvc; + if (bUpdate) + sAccessSrvc = "com.sun.star.configuration.ConfigurationUpdateAccess"; + else + sAccessSrvc = "com.sun.star.configuration.ConfigurationAccess"; + + OUString sConfigURL = OUString::createFromAscii(pPath); + + uno::Reference< XMultiServiceFactory > theConfigProvider( + configuration::theDefaultProvider::get( + comphelper::getProcessComponentContext())); + + // access the provider + uno::Sequence< uno::Any > theArgs {uno::Any(sConfigURL)}; + xNameAccess.set( + theConfigProvider->createInstanceWithArguments( + sAccessSrvc, theArgs ), uno::UNO_QUERY_THROW ); + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION("desktop.migration", "ignoring"); + } + return xNameAccess; +} + +void MigrationImpl::copyFiles() +{ + OUString localName; + OUString destName; + OUString userInstall; + utl::Bootstrap::PathStatus aStatus; + aStatus = utl::Bootstrap::locateUserInstallation(userInstall); + if (aStatus == utl::Bootstrap::PATH_EXISTS) { + for (auto const& rFile : *m_vrFileList) + { + // remove installation prefix from file + localName = rFile.copy(m_aInfo.userdata.getLength()); + if (localName.endsWith( "/autocorr/acor_.dat")) { + // Previous versions used an empty language tag for + // LANGUAGE_DONTKNOW with the "[All]" autocorrection entry. + // As of LibreOffice 4.0 it is 'und' for LANGUAGE_UNDETERMINED + // so the file name is "acor_und.dat". + localName = OUString::Concat(localName.subView( 0, localName.getLength() - 4)) + "und.dat"; + } + destName = userInstall + localName; + INetURLObject aURL(destName); + // check whether destination directory exists + aURL.removeSegment(); + _checkAndCreateDirectory(aURL); + FileBase::RC copyResult = File::copy(rFile, destName); + if (copyResult != FileBase::E_None) { + SAL_WARN( "desktop", "Cannot copy " << rFile << " to " << destName); + } + } + } else { + OSL_FAIL("copyFiles: UserInstall does not exist"); + } +} + +void MigrationImpl::runServices() +{ + // Build argument array + uno::Sequence< uno::Any > seqArguments(3); + auto pseqArguments = seqArguments.getArray(); + pseqArguments[0] <<= NamedValue("Productname", + uno::Any(m_aInfo.productname)); + pseqArguments[1] <<= NamedValue("UserData", + uno::Any(m_aInfo.userdata)); + + + // create an instance of every migration service + // and execute the migration job + uno::Reference< XJob > xMigrationJob; + + uno::Reference< uno::XComponentContext > xContext(comphelper::getProcessComponentContext()); + for (auto const& rMigration : *m_vrMigrations) + { + if( !rMigration.service.isEmpty()) { + + try { + // set black list for extension migration + uno::Sequence< OUString > seqExtDenyList; + sal_uInt32 nSize = rMigration.excludeExtensions.size(); + if ( nSize > 0 ) + seqExtDenyList = comphelper::arrayToSequence< OUString >( + rMigration.excludeExtensions.data(), nSize ); + pseqArguments[2] <<= NamedValue("ExtensionDenyList", + uno::Any( seqExtDenyList )); + + xMigrationJob.set( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext(rMigration.service, seqArguments, xContext), + uno::UNO_QUERY_THROW); + + xMigrationJob->execute(uno::Sequence< NamedValue >()); + + + } catch (const Exception&) { + TOOLS_WARN_EXCEPTION( "desktop", "Execution of migration service failed. Service: " + << rMigration.service); + } catch (...) { + SAL_WARN( "desktop", "Execution of migration service failed (Exception caught).\nService: " + << rMigration.service << "\nNo message available"); + } + + } + } +} + +std::vector< MigrationModuleInfo > MigrationImpl::detectUIChangesForAllModules() const +{ + std::vector< MigrationModuleInfo > vModulesInfo; + static const OUStringLiteral MENUBAR(u"menubar"); + static const OUStringLiteral TOOLBAR(u"toolbar"); + + uno::Sequence< uno::Any > lArgs {uno::Any(m_aInfo.userdata + "/user/config/soffice.cfg/modules"), + uno::Any(embed::ElementModes::READ)}; + + uno::Reference< lang::XSingleServiceFactory > xStorageFactory( + embed::FileSystemStorageFactory::create(comphelper::getProcessComponentContext())); + uno::Reference< embed::XStorage > xModules; + + xModules.set(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY); + if (!xModules.is()) + return vModulesInfo; + + uno::Sequence< OUString > lNames = xModules->getElementNames(); + sal_Int32 nLength = lNames.getLength(); + for (sal_Int32 i=0; i<nLength; ++i) { + OUString sModuleShortName = lNames[i]; + uno::Reference< embed::XStorage > xModule = xModules->openStorageElement(sModuleShortName, embed::ElementModes::READ); + if (xModule.is()) { + MigrationModuleInfo aModuleInfo; + + uno::Reference< embed::XStorage > xMenubar = xModule->openStorageElement(MENUBAR, embed::ElementModes::READ); + if (xMenubar.is()) { + if (xMenubar->getElementNames().hasElements()) { + aModuleInfo.sModuleShortName = sModuleShortName; + aModuleInfo.bHasMenubar = true; + } + } + + uno::Reference< embed::XStorage > xToolbar = xModule->openStorageElement(TOOLBAR, embed::ElementModes::READ); + if (xToolbar.is()) { + const ::uno::Sequence< OUString > lToolbars = xToolbar->getElementNames(); + for (OUString const & sToolbarName : lToolbars) { + if (sToolbarName.startsWith("custom_")) + continue; + + aModuleInfo.sModuleShortName = sModuleShortName; + sal_Int32 nIndex = sToolbarName.lastIndexOf('.'); + if (nIndex > 0) { + std::u16string_view sExtension(sToolbarName.subView(nIndex)); + OUString sToolbarResourceName(sToolbarName.copy(0, nIndex)); + if (!sToolbarResourceName.isEmpty() && sExtension == u".xml") + aModuleInfo.m_vToolbars.push_back(sToolbarResourceName); + } + } + } + + if (!aModuleInfo.sModuleShortName.isEmpty()) + vModulesInfo.push_back(aModuleInfo); + } + } + + return vModulesInfo; +} + +void MigrationImpl::compareOldAndNewConfig(const OUString& sParent, + const uno::Reference< container::XIndexContainer >& xIndexOld, + const uno::Reference< container::XIndexContainer >& xIndexNew, + const OUString& sResourceURL) +{ + static const OUStringLiteral MENU_SEPARATOR(u" | "); + + std::vector< MigrationItem > vOldItems; + std::vector< MigrationItem > vNewItems; + uno::Sequence< beans::PropertyValue > aProps; + sal_Int32 nOldCount = xIndexOld->getCount(); + sal_Int32 nNewCount = xIndexNew->getCount(); + + for (int n=0; n<nOldCount; ++n) { + MigrationItem aMigrationItem; + if (xIndexOld->getByIndex(n) >>= aProps) { + for(beans::PropertyValue const & prop : std::as_const(aProps)) { + if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL ) + prop.Value >>= aMigrationItem.m_sCommandURL; + else if ( prop.Name == ITEM_DESCRIPTOR_CONTAINER ) + prop.Value >>= aMigrationItem.m_xPopupMenu; + } + + if (!aMigrationItem.m_sCommandURL.isEmpty()) + vOldItems.push_back(aMigrationItem); + } + } + + for (int n=0; n<nNewCount; ++n) { + MigrationItem aMigrationItem; + if (xIndexNew->getByIndex(n) >>= aProps) { + for(beans::PropertyValue const & prop : std::as_const(aProps)) { + if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL ) + prop.Value >>= aMigrationItem.m_sCommandURL; + else if ( prop.Name == ITEM_DESCRIPTOR_CONTAINER ) + prop.Value >>= aMigrationItem.m_xPopupMenu; + } + + if (!aMigrationItem.m_sCommandURL.isEmpty()) + vNewItems.push_back(aMigrationItem); + } + } + + OUString sSibling; + for (auto const& oldItem : vOldItems) + { + std::vector< MigrationItem >::iterator pFound = std::find(vNewItems.begin(), vNewItems.end(), oldItem); + if (pFound != vNewItems.end() && oldItem.m_xPopupMenu.is()) { + OUString sName; + if (!sParent.isEmpty()) + sName = sParent + MENU_SEPARATOR + oldItem.m_sCommandURL; + else + sName = oldItem.m_sCommandURL; + compareOldAndNewConfig(sName, oldItem.m_xPopupMenu, pFound->m_xPopupMenu, sResourceURL); + } else if (pFound == vNewItems.end()) { + MigrationItem aMigrationItem(sParent, sSibling, oldItem.m_sCommandURL, oldItem.m_xPopupMenu); + if (m_aOldVersionItemsHashMap.find(sResourceURL)==m_aOldVersionItemsHashMap.end()) { + std::vector< MigrationItem > vMigrationItems; + m_aOldVersionItemsHashMap.emplace(sResourceURL, vMigrationItems); + m_aOldVersionItemsHashMap[sResourceURL].push_back(aMigrationItem); + } else { + if (std::find(m_aOldVersionItemsHashMap[sResourceURL].begin(), m_aOldVersionItemsHashMap[sResourceURL].end(), aMigrationItem)==m_aOldVersionItemsHashMap[sResourceURL].end()) + m_aOldVersionItemsHashMap[sResourceURL].push_back(aMigrationItem); + } + } + + sSibling = oldItem.m_sCommandURL; + } +} + +void MigrationImpl::mergeOldToNewVersion(const uno::Reference< ui::XUIConfigurationManager >& xCfgManager, + const uno::Reference< container::XIndexContainer>& xIndexContainer, + const OUString& sModuleIdentifier, + const OUString& sResourceURL) +{ + MigrationHashMap::iterator pFound = m_aOldVersionItemsHashMap.find(sResourceURL); + if (pFound==m_aOldVersionItemsHashMap.end()) + return; + + for (auto const& elem : pFound->second) + { + uno::Reference< container::XIndexContainer > xTemp = xIndexContainer; + + OUString sParentNodeName = elem.m_sParentNodeName; + sal_Int32 nIndex = 0; + do { + std::u16string_view sToken( o3tl::trim(o3tl::getToken(sParentNodeName, 0, '|', nIndex)) ); + if (sToken.empty()) + break; + + sal_Int32 nCount = xTemp->getCount(); + for (sal_Int32 i=0; i<nCount; ++i) { + OUString sCommandURL; + OUString sLabel; + uno::Reference< container::XIndexContainer > xChild; + + uno::Sequence< beans::PropertyValue > aPropSeq; + xTemp->getByIndex(i) >>= aPropSeq; + for (beans::PropertyValue const & prop : std::as_const(aPropSeq)) { + OUString sPropName = prop.Name; + if ( sPropName == ITEM_DESCRIPTOR_COMMANDURL ) + prop.Value >>= sCommandURL; + else if ( sPropName == ITEM_DESCRIPTOR_LABEL ) + prop.Value >>= sLabel; + else if ( sPropName == ITEM_DESCRIPTOR_CONTAINER ) + prop.Value >>= xChild; + } + + if (sCommandURL == sToken) { + xTemp = xChild; + break; + } + } + + } while (nIndex >= 0); + + if (nIndex == -1) { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(elem.m_sCommandURL, sModuleIdentifier); + uno::Sequence< beans::PropertyValue > aPropSeq { + beans::PropertyValue(ITEM_DESCRIPTOR_COMMANDURL, 0, uno::Any(elem.m_sCommandURL), beans::PropertyState_DIRECT_VALUE), + beans::PropertyValue(ITEM_DESCRIPTOR_LABEL, 0, uno::Any(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)), beans::PropertyState_DIRECT_VALUE), + beans::PropertyValue(ITEM_DESCRIPTOR_CONTAINER, 0, uno::Any(elem.m_xPopupMenu), beans::PropertyState_DIRECT_VALUE) + }; + + if (elem.m_sPrevSibling.isEmpty()) + xTemp->insertByIndex(0, uno::Any(aPropSeq)); + else { + sal_Int32 nCount = xTemp->getCount(); + sal_Int32 i = 0; + for (; i<nCount; ++i) { + OUString sCmd; + uno::Sequence< beans::PropertyValue > aTempPropSeq; + xTemp->getByIndex(i) >>= aTempPropSeq; + for (beans::PropertyValue const & prop : std::as_const(aTempPropSeq)) { + if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL ) { + prop.Value >>= sCmd; + break; + } + } + + if (sCmd == elem.m_sPrevSibling) + break; + } + + xTemp->insertByIndex(i+1, uno::Any(aPropSeq)); + } + } + } + + if (xIndexContainer.is()) + xCfgManager->replaceSettings(sResourceURL, xIndexContainer); + + uno::Reference< ui::XUIConfigurationPersistence > xUIConfigurationPersistence(xCfgManager, uno::UNO_QUERY); + if (xUIConfigurationPersistence.is()) + xUIConfigurationPersistence->store(); +} + +uno::Reference< ui::XUIConfigurationManager > NewVersionUIInfo::getConfigManager(std::u16string_view sModuleShortName) const +{ + uno::Reference< ui::XUIConfigurationManager > xCfgManager; + + for ( const css::beans::PropertyValue& rProp : m_lCfgManagerSeq) { + if (rProp.Name == sModuleShortName) { + rProp.Value >>= xCfgManager; + break; + } + } + + return xCfgManager; +} + +uno::Reference< container::XIndexContainer > NewVersionUIInfo::getNewMenubarSettings(std::u16string_view sModuleShortName) const +{ + uno::Reference< container::XIndexContainer > xNewMenuSettings; + + for (auto const & prop : m_lNewVersionMenubarSettingsSeq) { + if (prop.Name == sModuleShortName) { + prop.Value >>= xNewMenuSettings; + break; + } + } + + return xNewMenuSettings; +} + +uno::Reference< container::XIndexContainer > NewVersionUIInfo::getNewToolbarSettings(std::u16string_view sModuleShortName, std::u16string_view sToolbarName) const +{ + uno::Reference< container::XIndexContainer > xNewToolbarSettings; + + for (auto const & newProp : m_lNewVersionToolbarSettingsSeq) { + if (newProp.Name == sModuleShortName) { + uno::Sequence< beans::PropertyValue > lToolbarSettingsSeq; + newProp.Value >>= lToolbarSettingsSeq; + for (auto const & prop : std::as_const(lToolbarSettingsSeq)) { + if (prop.Name == sToolbarName) { + prop.Value >>= xNewToolbarSettings; + break; + } + } + + break; + } + } + + return xNewToolbarSettings; +} + +void NewVersionUIInfo::init(const std::vector< MigrationModuleInfo >& vModulesInfo) +{ + m_lCfgManagerSeq.resize(vModulesInfo.size()); + m_lNewVersionMenubarSettingsSeq.realloc(vModulesInfo.size()); + auto p_lNewVersionMenubarSettingsSeq = m_lNewVersionMenubarSettingsSeq.getArray(); + m_lNewVersionToolbarSettingsSeq.realloc(vModulesInfo.size()); + auto p_lNewVersionToolbarSettingsSeq = m_lNewVersionToolbarSettingsSeq.getArray(); + + static const OUStringLiteral sMenubarResourceURL(u"private:resource/menubar/menubar"); + static const OUStringLiteral sToolbarResourcePre(u"private:resource/toolbar/"); + + uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier = ui::theModuleUIConfigurationManagerSupplier::get( ::comphelper::getProcessComponentContext() ); + + for (size_t i=0; i<vModulesInfo.size(); ++i) { + OUString sModuleIdentifier = mapModuleShortNameToIdentifier(vModulesInfo[i].sModuleShortName); + if (!sModuleIdentifier.isEmpty()) { + uno::Reference< ui::XUIConfigurationManager > xCfgManager = xModuleCfgSupplier->getUIConfigurationManager(sModuleIdentifier); + m_lCfgManagerSeq[i].Name = vModulesInfo[i].sModuleShortName; + m_lCfgManagerSeq[i].Value <<= xCfgManager; + + if (vModulesInfo[i].bHasMenubar) { + p_lNewVersionMenubarSettingsSeq[i].Name = vModulesInfo[i].sModuleShortName; + p_lNewVersionMenubarSettingsSeq[i].Value <<= xCfgManager->getSettings(sMenubarResourceURL, true); + } + + sal_Int32 nToolbars = vModulesInfo[i].m_vToolbars.size(); + if (nToolbars > 0) { + uno::Sequence< beans::PropertyValue > lPropSeq(nToolbars); + auto plPropSeq = lPropSeq.getArray(); + for (sal_Int32 j=0; j<nToolbars; ++j) { + OUString sToolbarName = vModulesInfo[i].m_vToolbars[j]; + OUString sToolbarResourceURL = sToolbarResourcePre + sToolbarName; + + plPropSeq[j].Name = sToolbarName; + plPropSeq[j].Value <<= xCfgManager->getSettings(sToolbarResourceURL, true); + } + + p_lNewVersionToolbarSettingsSeq[i].Name = vModulesInfo[i].sModuleShortName; + p_lNewVersionToolbarSettingsSeq[i].Value <<= lPropSeq; + } + } + } +} + +} // namespace desktop + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/migration_impl.hxx b/desktop/source/migration/migration_impl.hxx new file mode 100644 index 000000000..6b0923d29 --- /dev/null +++ b/desktop/source/migration/migration_impl.hxx @@ -0,0 +1,198 @@ +/* -*- 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 . + */ +#pragma once + +#include <memory> +#include <string_view> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <o3tl/string_view.hxx> +#include <sal/types.h> +#include <rtl/ustring.hxx> + +#include <com/sun/star/uno/Reference.hxx> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> + +namespace desktop +{ + +struct install_info +{ + OUString productname; // human readable product name + OUString userdata; // file: url for user installation +}; + +typedef std::vector< OUString > strings_v; +typedef std::unique_ptr< strings_v > strings_vr; + +struct migration_step +{ + strings_v includeFiles; + strings_v excludeFiles; + strings_v includeConfig; + strings_v excludeConfig; + strings_v excludeExtensions; + OUString service; +}; + +struct supported_migration +{ + OUString name; + sal_Int32 nPriority; + strings_v supported_versions; +}; + +typedef std::vector< migration_step > migrations_v; +typedef std::unique_ptr< migrations_v > migrations_vr; +typedef std::vector< supported_migration > migrations_available; + +inline bool areBothOpenFrom(std::u16string_view cmd1, std::u16string_view cmd2) +{ + return cmd1 == u".uno:Open" && o3tl::starts_with(cmd2, u".uno:OpenFrom"); +} + +/** + define the item, e.g.:menuitem, toolbaritem, to be migrated. we keep the information + of the command URL, the previous sibling node and the parent node of an item +*/ +struct MigrationItem +{ + OUString m_sParentNodeName; + OUString m_sPrevSibling; + OUString m_sCommandURL; + css::uno::Reference< css::container::XIndexContainer > m_xPopupMenu; + + MigrationItem() + { + } + + MigrationItem(OUString sParentNodeName, + OUString sPrevSibling, + OUString sCommandURL, + css::uno::Reference< css::container::XIndexContainer > xPopupMenu) + : m_sParentNodeName(std::move(sParentNodeName)), m_sPrevSibling(std::move(sPrevSibling)), + m_sCommandURL(std::move(sCommandURL)), m_xPopupMenu(std::move(xPopupMenu)) + { + } + + bool operator==(const MigrationItem& aMigrationItem) const + { + return + (aMigrationItem.m_sCommandURL == m_sCommandURL + || areBothOpenFrom(aMigrationItem.m_sCommandURL, m_sCommandURL) + || areBothOpenFrom(m_sCommandURL, aMigrationItem.m_sCommandURL)) + && aMigrationItem.m_sParentNodeName == m_sParentNodeName + && aMigrationItem.m_sPrevSibling == m_sPrevSibling + && aMigrationItem.m_xPopupMenu.is() == m_xPopupMenu.is(); + } +}; + +typedef std::unordered_map< OUString, std::vector< MigrationItem > > MigrationHashMap; + +/** + information for the UI elements to be migrated for one module +*/ +struct MigrationModuleInfo +{ + OUString sModuleShortName; + bool bHasMenubar; + std::vector< OUString > m_vToolbars; + + MigrationModuleInfo() : bHasMenubar(false) {}; +}; + + +/** + get the information before copying the ui configuration files of old version to new version +*/ +class NewVersionUIInfo +{ +public: + + css::uno::Reference< css::ui::XUIConfigurationManager > getConfigManager(std::u16string_view sModuleShortName) const; + css::uno::Reference< css::container::XIndexContainer > getNewMenubarSettings(std::u16string_view sModuleShortName) const; + css::uno::Reference< css::container::XIndexContainer > getNewToolbarSettings(std::u16string_view sModuleShortName, std::u16string_view sToolbarName) const; + void init(const std::vector< MigrationModuleInfo >& vModulesInfo); + +private: + + std::vector< css::beans::PropertyValue > m_lCfgManagerSeq; + css::uno::Sequence< css::beans::PropertyValue > m_lNewVersionMenubarSettingsSeq; + css::uno::Sequence< css::beans::PropertyValue > m_lNewVersionToolbarSettingsSeq; +}; + +class MigrationImpl +{ + +private: + migrations_available m_vMigrationsAvailable; // list of all available migrations + migrations_vr m_vrMigrations; // list of all migration specs from config + install_info m_aInfo; // info about the version being migrated + strings_vr m_vrFileList; // final list of files to be copied + MigrationHashMap m_aOldVersionItemsHashMap; + + // functions to control the migration process + static void readAvailableMigrations(migrations_available&); + bool alreadyMigrated(); + static migrations_vr readMigrationSteps(const OUString& rMigrationName); + sal_Int32 findPreferredMigrationProcess(const migrations_available&); +#if defined UNX && ! defined MACOSX + static OUString preXDGConfigDir(const OUString& rConfigDir); +#endif + static void setInstallInfoIfExist(install_info& aInfo, std::u16string_view rConfigDir, const OUString& rVersion); + static install_info findInstallation(const strings_v& rVersions); + strings_vr compileFileList(); + + // helpers + strings_vr getAllFiles(const OUString& baseURL) const; + static strings_vr applyPatterns(const strings_v& vSet, const strings_v& vPatterns); + static css::uno::Reference< css::container::XNameAccess > getConfigAccess(const char* path, bool rw=false); + + std::vector< MigrationModuleInfo > detectUIChangesForAllModules() const; + void compareOldAndNewConfig(const OUString& sParentNodeName, + const css::uno::Reference< css::container::XIndexContainer >& xOldIndexContainer, + const css::uno::Reference< css::container::XIndexContainer >& xNewIndexContainer, + const OUString& sToolbarName); + void mergeOldToNewVersion(const css::uno::Reference< css::ui::XUIConfigurationManager >& xCfgManager, + const css::uno::Reference< css::container::XIndexContainer>& xIndexContainer, + const OUString& sModuleIdentifier, + const OUString& sResourceURL); + + // actual processing function that perform the migration steps + void copyFiles(); + void copyConfig(); + void runServices(); + + static void setMigrationCompleted(); + static bool checkMigrationCompleted(); + +public: + MigrationImpl(); + ~MigrationImpl(); + bool initializeMigration(); + bool doMigration(); +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/basicmigration.cxx b/desktop/source/migration/services/basicmigration.cxx new file mode 100644 index 000000000..94e8677de --- /dev/null +++ b/desktop/source/migration/services/basicmigration.cxx @@ -0,0 +1,202 @@ +/* -*- 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 "basicmigration.hxx" +#include <cppuhelper/supportsservice.hxx> +#include <tools/urlobj.hxx> +#include <unotools/bootstrap.hxx> +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + + +namespace migration +{ + + + #define sSourceUserBasic "/user/basic" + #define sTargetUserBasic "/user/__basic_80" + + + // BasicMigration + + + BasicMigration::BasicMigration() + { + } + + + BasicMigration::~BasicMigration() + { + } + + + TStringVectorPtr BasicMigration::getFiles( const OUString& rBaseURL ) const + { + TStringVectorPtr aResult( new TStringVector ); + ::osl::Directory aDir( rBaseURL); + + if ( aDir.open() == ::osl::FileBase::E_None ) + { + // iterate over directory content + TStringVector aSubDirs; + ::osl::DirectoryItem aItem; + while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None ) + { + ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL ); + if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None ) + { + if ( aFileStatus.getFileType() == ::osl::FileStatus::Directory ) + aSubDirs.push_back( aFileStatus.getFileURL() ); + else + aResult->push_back( aFileStatus.getFileURL() ); + } + } + + // iterate recursive over subfolders + for (auto const& subDir : aSubDirs) + { + TStringVectorPtr aSubResult = getFiles(subDir); + aResult->insert( aResult->end(), aSubResult->begin(), aSubResult->end() ); + } + } + + return aResult; + } + + + void BasicMigration::checkAndCreateDirectory( INetURLObject const & rDirURL ) + { + ::osl::FileBase::RC aResult = ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) ); + if ( aResult == ::osl::FileBase::E_NOENT ) + { + INetURLObject aBaseURL( rDirURL ); + aBaseURL.removeSegment(); + checkAndCreateDirectory( aBaseURL ); + ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) ); + } + } + + + void BasicMigration::copyFiles() + { + OUString sTargetDir; + ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( sTargetDir ); + if ( aStatus == ::utl::Bootstrap::PATH_EXISTS ) + { + sTargetDir += sTargetUserBasic; + TStringVectorPtr aFileList = getFiles( m_sSourceDir ); + for (auto const& elem : *aFileList) + { + std::u16string_view sLocalName = elem.subView( m_sSourceDir.getLength() ); + OUString sTargetName = sTargetDir + sLocalName; + INetURLObject aURL( sTargetName ); + aURL.removeSegment(); + checkAndCreateDirectory( aURL ); + ::osl::FileBase::RC aResult = ::osl::File::copy( elem, sTargetName ); + if ( aResult != ::osl::FileBase::E_None ) + { + SAL_WARN( "desktop", "BasicMigration::copyFiles: cannot copy " + << elem << " to " << sTargetName ); + } + } + } + else + { + OSL_FAIL( "BasicMigration::copyFiles: no user installation!" ); + } + } + + + // XServiceInfo + + + OUString BasicMigration::getImplementationName() + { + return "com.sun.star.comp.desktop.migration.Basic"; + } + + + sal_Bool BasicMigration::supportsService(OUString const & ServiceName) + { + return cppu::supportsService(this, ServiceName); + } + + + Sequence< OUString > BasicMigration::getSupportedServiceNames() + { + return { "com.sun.star.migration.Basic" }; + } + + + // XInitialization + + + void BasicMigration::initialize( const Sequence< Any >& aArguments ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + const Any* pIter = aArguments.getConstArray(); + const Any* pEnd = pIter + aArguments.getLength(); + for ( ; pIter != pEnd ; ++pIter ) + { + beans::NamedValue aValue; + *pIter >>= aValue; + if ( aValue.Name == "UserData" ) + { + if ( !(aValue.Value >>= m_sSourceDir) ) + { + OSL_FAIL( "BasicMigration::initialize: argument UserData has wrong type!" ); + } + m_sSourceDir += sSourceUserBasic; + break; + } + } + } + + + // XJob + + + Any BasicMigration::execute( const Sequence< beans::NamedValue >& ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + copyFiles(); + + return Any(); + } + + +} // namespace migration + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +desktop_BasicMigration_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new migration::BasicMigration()); +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/basicmigration.hxx b/desktop/source/migration/services/basicmigration.hxx new file mode 100644 index 000000000..889b9245d --- /dev/null +++ b/desktop/source/migration/services/basicmigration.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include "misc.hxx" +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/task/XJob.hpp> +#include <cppuhelper/implbase.hxx> +#include <osl/mutex.hxx> + + +class INetURLObject; + + +namespace migration +{ + typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::task::XJob > BasicMigration_BASE; + + class BasicMigration : public BasicMigration_BASE + { + private: + ::osl::Mutex m_aMutex; + OUString m_sSourceDir; + + TStringVectorPtr getFiles( const OUString& rBaseURL ) const; + void checkAndCreateDirectory( INetURLObject const & rDirURL ); + void copyFiles(); + + public: + BasicMigration(); + virtual ~BasicMigration() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XJob + virtual css::uno::Any SAL_CALL execute( + const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override; + }; + + +} // namespace migration + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/cppumaker.mk b/desktop/source/migration/services/cppumaker.mk new file mode 100644 index 000000000..57e070f80 --- /dev/null +++ b/desktop/source/migration/services/cppumaker.mk @@ -0,0 +1,27 @@ +# +# 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 . +# + +.IF "$(debug)" != "" + +# MSVC++: no inlining +.IF "$(COM)" == "MSC" +CFLAGS += -Ob0 +.ENDIF + +.ENDIF + diff --git a/desktop/source/migration/services/jvmfwk.cxx b/desktop/source/migration/services/jvmfwk.cxx new file mode 100644 index 000000000..65eff6767 --- /dev/null +++ b/desktop/source/migration/services/jvmfwk.cxx @@ -0,0 +1,395 @@ +/* -*- 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 <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ustring.hxx> +#include <rtl/bootstrap.hxx> +#include <sal/types.h> +#include <sal/config.h> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/WrappedTargetException.hpp> +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/configuration/backend/XLayer.hpp> +#include <com/sun/star/configuration/backend/XLayerHandler.hpp> +#include <com/sun/star/configuration/backend/MalformedDataException.hpp> +#include <com/sun/star/configuration/backend/TemplateIdentifier.hpp> +#include <jvmfwk/framework.hxx> +#include "jvmfwk.hxx" +#include <memory> +#include <stack> +#include <stdio.h> + +#include <osl/diagnose.h> + +constexpr OUStringLiteral SERVICE_NAME = u"com.sun.star.migration.Java"; +#define IMPL_NAME "com.sun.star.comp.desktop.migration.Java" + +#define ENABLE_JAVA 1 +#define USER_CLASS_PATH 2 + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::configuration::backend; + +namespace migration +{ + +namespace { + +class JavaMigration : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::task::XJob, + css::configuration::backend::XLayerHandler> +{ +public: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + //XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + //XJob + virtual css::uno::Any SAL_CALL execute( + const css::uno::Sequence<css::beans::NamedValue >& Arguments ) override; + + // XLayerHandler + virtual void SAL_CALL startLayer() override; + + virtual void SAL_CALL endLayer() override; + + virtual void SAL_CALL overrideNode( + const OUString& aName, + sal_Int16 aAttributes, + sal_Bool bClear) override; + + virtual void SAL_CALL addOrReplaceNode( + const OUString& aName, + sal_Int16 aAttributes) override; + + virtual void SAL_CALL addOrReplaceNodeFromTemplate( + const OUString& aName, + const css::configuration::backend::TemplateIdentifier& aTemplate, + sal_Int16 aAttributes ) override; + + virtual void SAL_CALL endNode() override; + + virtual void SAL_CALL dropNode( + const OUString& aName ) override; + + virtual void SAL_CALL overrideProperty( + const OUString& aName, + sal_Int16 aAttributes, + const css::uno::Type& aType, + sal_Bool bClear ) override; + + virtual void SAL_CALL setPropertyValue( + const css::uno::Any& aValue ) override; + + virtual void SAL_CALL setPropertyValueForLocale( + const css::uno::Any& aValue, + const OUString& aLocale ) override; + + virtual void SAL_CALL endProperty() override; + + virtual void SAL_CALL addProperty( + const OUString& aName, + sal_Int16 aAttributes, + const css::uno::Type& aType ) override; + + virtual void SAL_CALL addPropertyWithValue( + const OUString& aName, + sal_Int16 aAttributes, + const css::uno::Any& aValue ) override; + + + virtual ~JavaMigration() override; + +private: + OUString m_sUserDir; + css::uno::Reference< css::configuration::backend::XLayer> m_xLayer; + + void migrateJavarc(); + typedef std::pair< OUString, sal_Int16> TElementType; + typedef std::stack< TElementType > TElementStack; + TElementStack m_aStack; + +}; + +} + +JavaMigration::~JavaMigration() +{ + OSL_ASSERT(m_aStack.empty()); +} + +OUString jvmfwk_getImplementationName() +{ + return IMPL_NAME; +} + +css::uno::Sequence< OUString > jvmfwk_getSupportedServiceNames() +{ + return { SERVICE_NAME }; +} + +// XServiceInfo +OUString SAL_CALL JavaMigration::getImplementationName() +{ + return jvmfwk_getImplementationName(); +} + +sal_Bool JavaMigration::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL JavaMigration::getSupportedServiceNames() +{ + return jvmfwk_getSupportedServiceNames(); +} + +//XInitialization ---------------------------------------------------------------------- +void SAL_CALL JavaMigration::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + const css::uno::Any* pIter = aArguments.getConstArray(); + const css::uno::Any* pEnd = pIter + aArguments.getLength(); + css::uno::Sequence<css::beans::NamedValue> aOldConfigValues; + css::beans::NamedValue aValue; + for(;pIter != pEnd;++pIter) + { + *pIter >>= aValue; + if ( aValue.Name == "OldConfiguration" ) + { + bool bSuccess = aValue.Value >>= aOldConfigValues; + OSL_ENSURE(bSuccess, "[Service implementation " IMPL_NAME + "] XInitialization::initialize: Argument OldConfiguration has wrong type."); + if (bSuccess) + { + const css::beans::NamedValue* pIter2 = aOldConfigValues.getConstArray(); + const css::beans::NamedValue* pEnd2 = pIter2 + aOldConfigValues.getLength(); + for(;pIter2 != pEnd2;++pIter2) + { + if ( pIter2->Name == "org.openoffice.Office.Java" ) + { + pIter2->Value >>= m_xLayer; + break; + } + } + } + } + else if ( aValue.Name == "UserData" ) + { + if ( !(aValue.Value >>= m_sUserDir) ) + { + OSL_FAIL( + "[Service implementation " IMPL_NAME + "] XInitialization::initialize: Argument UserData has wrong type."); + } + } + } + +} + +//XJob +css::uno::Any SAL_CALL JavaMigration::execute( + const css::uno::Sequence<css::beans::NamedValue >& ) +{ + migrateJavarc(); + if (m_xLayer.is()) + m_xLayer->readData(this); + + return css::uno::Any(); +} + +void JavaMigration::migrateJavarc() +{ + if (m_sUserDir.isEmpty()) + return; + + OUString sValue; + rtl::Bootstrap javaini(m_sUserDir + "/user/config/" SAL_CONFIGFILE("java")); + bool bSuccess = javaini.getFrom("Home", sValue); + OSL_ENSURE(bSuccess, "[Service implementation " IMPL_NAME + "] XJob::execute: Could not get Home entry from java.ini/javarc."); + if (!bSuccess || sValue.isEmpty()) + return; + + //get the directory + std::unique_ptr<JavaInfo> aInfo; + javaFrameworkError err = jfw_getJavaInfoByPath(sValue, &aInfo); + + if (err == JFW_E_NONE) + { + if (jfw_setSelectedJRE(aInfo.get()) != JFW_E_NONE) + { + OSL_FAIL("[Service implementation " IMPL_NAME + "] XJob::execute: jfw_setSelectedJRE failed."); + fprintf(stderr, "\nCannot migrate Java. An error occurred.\n"); + } + } + else if (err == JFW_E_FAILED_VERSION) + { + fprintf(stderr, "\nCannot migrate Java settings because the version of the Java " + "is not supported anymore.\n"); + } +} + + +// XLayerHandler +void SAL_CALL JavaMigration::startLayer() +{ +} + + +void SAL_CALL JavaMigration::endLayer() +{ +} + + +void SAL_CALL JavaMigration::overrideNode( + const OUString&, + sal_Int16, + sal_Bool) + +{ + +} + + +void SAL_CALL JavaMigration::addOrReplaceNode( + const OUString&, + sal_Int16) +{ + +} +void SAL_CALL JavaMigration::endNode() +{ +} + + +void SAL_CALL JavaMigration::dropNode( + const OUString& ) +{ +} + + +void SAL_CALL JavaMigration::overrideProperty( + const OUString& aName, + sal_Int16, + const Type&, + sal_Bool ) +{ + if ( aName == "Enable" ) + m_aStack.push(TElementStack::value_type(aName,ENABLE_JAVA)); + else if ( aName == "UserClassPath" ) + m_aStack.push(TElementStack::value_type(aName, USER_CLASS_PATH)); +} + + +void SAL_CALL JavaMigration::setPropertyValue( + const Any& aValue ) +{ + if ( m_aStack.empty()) + return; + + switch (m_aStack.top().second) + { + case ENABLE_JAVA: + { + bool val; + if (!(aValue >>= val)) + throw MalformedDataException( + "[Service implementation " IMPL_NAME + "] XLayerHandler::setPropertyValue received wrong type for Enable property", nullptr, Any()); + if (jfw_setEnabled(val) != JFW_E_NONE) + throw WrappedTargetException( + "[Service implementation " IMPL_NAME + "] XLayerHandler::setPropertyValue: jfw_setEnabled failed.", nullptr, Any()); + + break; + } + case USER_CLASS_PATH: + { + OUString cp; + if (!(aValue >>= cp)) + throw MalformedDataException( + "[Service implementation " IMPL_NAME + "] XLayerHandler::setPropertyValue received wrong type for UserClassPath property", nullptr, Any()); + + if (jfw_setUserClassPath(cp) != JFW_E_NONE) + throw WrappedTargetException( + "[Service implementation " IMPL_NAME + "] XLayerHandler::setPropertyValue: jfw_setUserClassPath failed.", nullptr, Any()); + break; + } + default: + OSL_ASSERT(false); + } +} + + +void SAL_CALL JavaMigration::setPropertyValueForLocale( + const Any&, + const OUString& ) +{ +} + + +void SAL_CALL JavaMigration::endProperty() +{ + if (!m_aStack.empty()) + m_aStack.pop(); +} + + +void SAL_CALL JavaMigration::addProperty( + const OUString&, + sal_Int16, + const Type& ) +{ +} + + +void SAL_CALL JavaMigration::addPropertyWithValue( + const OUString&, + sal_Int16, + const Any& ) +{ +} + +void SAL_CALL JavaMigration::addOrReplaceNodeFromTemplate( + const OUString&, + const TemplateIdentifier&, + sal_Int16 ) +{ +} + + +//ToDo enable java, user class path + +} //end namespace jfw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/jvmfwk.hxx b/desktop/source/migration/services/jvmfwk.hxx new file mode 100644 index 000000000..63ec7e717 --- /dev/null +++ b/desktop/source/migration/services/jvmfwk.hxx @@ -0,0 +1,33 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +namespace migration +{ +OUString jvmfwk_getImplementationName(); + +css::uno::Sequence<OUString> jvmfwk_getSupportedServiceNames(); + +} //end blind namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/migrationoo2.component b/desktop/source/migration/services/migrationoo2.component new file mode 100644 index 000000000..255023566 --- /dev/null +++ b/desktop/source/migration/services/migrationoo2.component @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.desktop.migration.Basic" + constructor="desktop_BasicMigration_get_implementation"> + <service name="com.sun.star.migration.Basic"/> + </implementation> + <implementation name="com.sun.star.comp.desktop.migration.Wordbooks" + constructor="desktop_WordbookMigration_get_implementation"> + <service name="com.sun.star.migration.Wordbooks"/> + </implementation> +</component> diff --git a/desktop/source/migration/services/migrationoo3.component b/desktop/source/migration/services/migrationoo3.component new file mode 100644 index 000000000..74432e586 --- /dev/null +++ b/desktop/source/migration/services/migrationoo3.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.desktop.migration.OOo3Extensions" + constructor="desktop_OO3ExtensionMigration_get_implementation"> + <service name="com.sun.star.migration.Extensions"/> + </implementation> +</component> diff --git a/desktop/source/migration/services/misc.hxx b/desktop/source/migration/services/misc.hxx new file mode 100644 index 000000000..c3b83b82e --- /dev/null +++ b/desktop/source/migration/services/misc.hxx @@ -0,0 +1,39 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> + +#include <vector> +#include <memory> + + +namespace migration +{ + + + typedef std::vector< OUString > TStringVector; + typedef std::unique_ptr< TStringVector > TStringVectorPtr; + + +} // namespace migration + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/oo3extensionmigration.cxx b/desktop/source/migration/services/oo3extensionmigration.cxx new file mode 100644 index 000000000..a3f9e02b6 --- /dev/null +++ b/desktop/source/migration/services/oo3extensionmigration.cxx @@ -0,0 +1,409 @@ +/* -*- 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 "oo3extensionmigration.hxx" +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/bootstrap.hxx> +#include <unotools/textsearch.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ref.hxx> + +#include <com/sun/star/task/XInteractionApprove.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/xml/xpath/XPathAPI.hpp> +#include <com/sun/star/xml/xpath/XPathException.hpp> +#include <com/sun/star/xml/dom/DOMException.hpp> +#include <com/sun/star/xml/dom/DocumentBuilder.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/deployment/ExtensionManager.hpp> +#include <com/sun/star/deployment/XExtensionManager.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace migration +{ + +// ExtensionMigration + + +OO3ExtensionMigration::OO3ExtensionMigration(Reference< XComponentContext > const & ctx) : +m_ctx(ctx) +{ +} + + +OO3ExtensionMigration::~OO3ExtensionMigration() +{ +} + +void OO3ExtensionMigration::scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions ) +{ + osl::Directory aScanRootDir( sSourceDir ); + osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL); + osl::FileBase::RC nRetCode = aScanRootDir.open(); + if ( nRetCode != osl::Directory::E_None ) + return; + + sal_uInt32 nHint( 0 ); + osl::DirectoryItem aItem; + while ( aScanRootDir.getNextItem( aItem, nHint ) == osl::Directory::E_None ) + { + if (( aItem.getFileStatus(fs) == osl::FileBase::E_None ) && + ( fs.getFileType() == osl::FileStatus::Directory )) + { + //Check next folder as the "real" extension folder is below a temp folder! + OUString sExtensionFolderURL = fs.getFileURL(); + + osl::Directory aExtensionRootDir( sExtensionFolderURL ); + + nRetCode = aExtensionRootDir.open(); + if ( nRetCode == osl::Directory::E_None ) + { + osl::DirectoryItem aExtDirItem; + while ( aExtensionRootDir.getNextItem( aExtDirItem, nHint ) == osl::Directory::E_None ) + { + bool bFileStatus = aExtDirItem.getFileStatus(fs) == osl::FileBase::E_None; + bool bIsDir = fs.getFileType() == osl::FileStatus::Directory; + + if ( bFileStatus && bIsDir ) + { + sExtensionFolderURL = fs.getFileURL(); + ScanResult eResult = scanExtensionFolder( sExtensionFolderURL ); + if ( eResult == SCANRESULT_MIGRATE_EXTENSION ) + aMigrateExtensions.push_back( sExtensionFolderURL ); + break; + } + } + } + } + } +} + +OO3ExtensionMigration::ScanResult OO3ExtensionMigration::scanExtensionFolder( const OUString& sExtFolder ) +{ + ScanResult aResult = SCANRESULT_NOTFOUND; + osl::Directory aDir(sExtFolder); + + // get sub dirs + if (aDir.open() == osl::FileBase::E_None) + { + // work through directory contents... + osl::DirectoryItem item; + osl::FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL); + TStringVector aDirectories; + while ((aDir.getNextItem(item) == osl::FileBase::E_None ) && + ( aResult == SCANRESULT_NOTFOUND )) + { + if (item.getFileStatus(fs) == osl::FileBase::E_None) + { + if (fs.getFileType() == osl::FileStatus::Directory) + aDirectories.push_back( fs.getFileURL() ); + else + { + OUString aDirEntryURL = fs.getFileURL(); + if ( aDirEntryURL.indexOf( "/description.xml" ) > 0 ) + aResult = scanDescriptionXml( aDirEntryURL ) ? SCANRESULT_MIGRATE_EXTENSION : SCANRESULT_DONTMIGRATE_EXTENSION; + } + } + } + + for (auto const& directory : aDirectories) + { + aResult = scanExtensionFolder(directory); + if (aResult != SCANRESULT_NOTFOUND) + break; + } + } + return aResult; +} + +bool OO3ExtensionMigration::scanDescriptionXml( const OUString& sDescriptionXmlURL ) +{ + if ( !m_xDocBuilder.is() ) + { + m_xDocBuilder.set( xml::dom::DocumentBuilder::create(m_ctx) ); + } + + if ( !m_xSimpleFileAccess.is() ) + { + m_xSimpleFileAccess = ucb::SimpleFileAccess::create(m_ctx); + } + + OUString aExtIdentifier; + try + { + uno::Reference< io::XInputStream > xIn = + m_xSimpleFileAccess->openFileRead( sDescriptionXmlURL ); + + if ( xIn.is() ) + { + uno::Reference< xml::dom::XDocument > xDoc = m_xDocBuilder->parse( xIn ); + if ( xDoc.is() ) + { + uno::Reference< xml::dom::XElement > xRoot = xDoc->getDocumentElement(); + if ( xRoot.is() && xRoot->getTagName() == "description" ) + { + uno::Reference< xml::xpath::XXPathAPI > xPath = xml::xpath::XPathAPI::create(m_ctx); + + xPath->registerNS("desc", xRoot->getNamespaceURI()); + xPath->registerNS("xlink", "http://www.w3.org/1999/xlink"); + + try + { + uno::Reference< xml::dom::XNode > xNode( + xPath->selectSingleNode( + xRoot, "desc:identifier/@value" )); + if ( xNode.is() ) + aExtIdentifier = xNode->getNodeValue(); + } + catch ( const xml::xpath::XPathException& ) + { + } + catch ( const xml::dom::DOMException& ) + { + } + } + } + } + + if ( !aExtIdentifier.isEmpty() ) + { + // scan extension identifier and try to match with our black list entries + for (const OUString & i : m_aDenyList) + { + utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp); + utl::TextSearch ts(param, LANGUAGE_DONTKNOW); + + sal_Int32 start = 0; + sal_Int32 end = aExtIdentifier.getLength(); + if (ts.SearchForward(aExtIdentifier, &start, &end)) + return false; + } + } + } + catch ( const ucb::CommandAbortedException& ) + { + } + catch ( const uno::RuntimeException& ) + { + } + + if ( aExtIdentifier.isEmpty() ) + { + // Fallback: + // Try to use the folder name to match our black list + // as some extensions don't provide an identifier in the + // description.xml! + for (const OUString & i : m_aDenyList) + { + utl::SearchParam param(i, utl::SearchParam::SearchType::Regexp); + utl::TextSearch ts(param, LANGUAGE_DONTKNOW); + + sal_Int32 start = 0; + sal_Int32 end = sDescriptionXmlURL.getLength(); + if (ts.SearchForward(sDescriptionXmlURL, &start, &end)) + return false; + } + } + + return true; +} + +void OO3ExtensionMigration::migrateExtension( const OUString& sSourceDir ) +{ + css::uno::Reference< css::deployment::XExtensionManager > extMgr( + deployment::ExtensionManager::get( m_ctx ) ); + try + { + rtl::Reference<TmpRepositoryCommandEnv> pCmdEnv = new TmpRepositoryCommandEnv(); + + uno::Reference< task::XAbortChannel > xAbortChannel; + extMgr->addExtension( + sSourceDir, uno::Sequence<beans::NamedValue>(), "user", + xAbortChannel, pCmdEnv ); + } + catch ( css::uno::Exception & ) + { + TOOLS_WARN_EXCEPTION( + "desktop.migration", + "Ignoring UNO Exception while migrating extension from <" << sSourceDir << ">"); + } +} + + +// XServiceInfo + + +OUString OO3ExtensionMigration::getImplementationName() +{ + return "com.sun.star.comp.desktop.migration.OOo3Extensions"; +} + + +sal_Bool OO3ExtensionMigration::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + + +Sequence< OUString > OO3ExtensionMigration::getSupportedServiceNames() +{ + return { "com.sun.star.migration.Extensions" }; +} + + +// XInitialization + + +void OO3ExtensionMigration::initialize( const Sequence< Any >& aArguments ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + const Any* pIter = aArguments.getConstArray(); + const Any* pEnd = pIter + aArguments.getLength(); + for ( ; pIter != pEnd ; ++pIter ) + { + beans::NamedValue aValue; + *pIter >>= aValue; + if ( aValue.Name == "UserData" ) + { + if ( !(aValue.Value >>= m_sSourceDir) ) + { + OSL_FAIL( "ExtensionMigration::initialize: argument UserData has wrong type!" ); + } + } + else if ( aValue.Name == "ExtensionDenyList" ) + { + Sequence< OUString > aDenyList; + if ( (aValue.Value >>= aDenyList ) && aDenyList.hasElements()) + { + m_aDenyList.resize( aDenyList.getLength() ); + ::comphelper::sequenceToArray< OUString >( m_aDenyList.data(), aDenyList ); + } + } + } +} + +Any OO3ExtensionMigration::execute( const Sequence< beans::NamedValue >& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( m_sTargetDir ); + if ( aStatus == ::utl::Bootstrap::PATH_EXISTS ) + { + // copy all extensions + OUString sSourceDir = m_sSourceDir + + "/user/uno_packages/cache/uno_packages"; + TStringVector aExtensionToMigrate; + scanUserExtensions( sSourceDir, aExtensionToMigrate ); + for (auto const& extensionToMigrate : aExtensionToMigrate) + { + migrateExtension(extensionToMigrate); + } + } + + return Any(); +} + + +// TmpRepositoryCommandEnv + + +TmpRepositoryCommandEnv::TmpRepositoryCommandEnv() +{ +} + +TmpRepositoryCommandEnv::~TmpRepositoryCommandEnv() +{ +} +// XCommandEnvironment + +uno::Reference< task::XInteractionHandler > TmpRepositoryCommandEnv::getInteractionHandler() +{ + return this; +} + + +uno::Reference< ucb::XProgressHandler > TmpRepositoryCommandEnv::getProgressHandler() +{ + return this; +} + +// XInteractionHandler +void TmpRepositoryCommandEnv::handle( + uno::Reference< task::XInteractionRequest> const & xRequest ) +{ + OSL_ASSERT( xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION ); + + bool approve = true; + + // select: + uno::Sequence< Reference< task::XInteractionContinuation > > conts( + xRequest->getContinuations() ); + Reference< task::XInteractionContinuation > const * pConts = + conts.getConstArray(); + sal_Int32 len = conts.getLength(); + for ( sal_Int32 pos = 0; pos < len; ++pos ) + { + if (approve) { + uno::Reference< task::XInteractionApprove > xInteractionApprove( + pConts[ pos ], uno::UNO_QUERY ); + if (xInteractionApprove.is()) { + xInteractionApprove->select(); + // don't query again for ongoing continuations: + approve = false; + } + } + } +} + +// XProgressHandler +void TmpRepositoryCommandEnv::push( uno::Any const & /*Status*/ ) +{ +} + + +void TmpRepositoryCommandEnv::update( uno::Any const & /*Status */) +{ +} + +void TmpRepositoryCommandEnv::pop() +{ +} + + +} // namespace migration + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +desktop_OO3ExtensionMigration_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new migration::OO3ExtensionMigration(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/oo3extensionmigration.hxx b/desktop/source/migration/services/oo3extensionmigration.hxx new file mode 100644 index 000000000..586e7e99e --- /dev/null +++ b/desktop/source/migration/services/oo3extensionmigration.hxx @@ -0,0 +1,117 @@ +/* -*- 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 . + */ + +#pragma once + +#include "misc.hxx" +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/xml/dom/XDocumentBuilder.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess3.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/XProgressHandler.hpp> + +#include <osl/mutex.hxx> +#include <cppuhelper/implbase.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +class INetURLObject; + + +namespace migration +{ + + typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::task::XJob > ExtensionMigration_BASE; + + class OO3ExtensionMigration : public ExtensionMigration_BASE + { + private: + css::uno::Reference< css::uno::XComponentContext > m_ctx; + css::uno::Reference< css::xml::dom::XDocumentBuilder > m_xDocBuilder; + css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSimpleFileAccess; + ::osl::Mutex m_aMutex; + OUString m_sSourceDir; + OUString m_sTargetDir; + TStringVector m_aDenyList; + + enum ScanResult + { + SCANRESULT_NOTFOUND, + SCANRESULT_MIGRATE_EXTENSION, + SCANRESULT_DONTMIGRATE_EXTENSION + }; + + ScanResult scanExtensionFolder( const OUString& sExtFolder ); + void scanUserExtensions( const OUString& sSourceDir, TStringVector& aMigrateExtensions ); + bool scanDescriptionXml( const OUString& sDescriptionXmlFilePath ); + void migrateExtension( const OUString& sSourceDir ); + + public: + explicit OO3ExtensionMigration(css::uno::Reference< + css::uno::XComponentContext > const & ctx); + virtual ~OO3ExtensionMigration() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XJob + virtual css::uno::Any SAL_CALL execute( + const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override; + }; + + class TmpRepositoryCommandEnv + : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment, + css::task::XInteractionHandler, + css::ucb::XProgressHandler > + { + public: + virtual ~TmpRepositoryCommandEnv() override; + TmpRepositoryCommandEnv(); + + // XCommandEnvironment + virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL + getInteractionHandler() override; + virtual css::uno::Reference< css::ucb::XProgressHandler > + SAL_CALL getProgressHandler() override; + + // XInteractionHandler + virtual void SAL_CALL handle( + css::uno::Reference< css::task::XInteractionRequest > const & xRequest ) override; + + // XProgressHandler + virtual void SAL_CALL push( css::uno::Any const & Status ) override; + virtual void SAL_CALL update( css::uno::Any const & Status ) override; + virtual void SAL_CALL pop() override; + }; + + +} // namespace migration + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/wordbookmigration.cxx b/desktop/source/migration/services/wordbookmigration.cxx new file mode 100644 index 000000000..a3fff8823 --- /dev/null +++ b/desktop/source/migration/services/wordbookmigration.cxx @@ -0,0 +1,231 @@ +/* -*- 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 "wordbookmigration.hxx" +#include <cppuhelper/supportsservice.hxx> +#include <tools/urlobj.hxx> +#include <unotools/bootstrap.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + + +namespace migration +{ + WordbookMigration::WordbookMigration() + { + } + + + WordbookMigration::~WordbookMigration() + { + } + + + TStringVectorPtr WordbookMigration::getFiles( const OUString& rBaseURL ) const + { + TStringVectorPtr aResult( new TStringVector ); + ::osl::Directory aDir( rBaseURL); + + if ( aDir.open() == ::osl::FileBase::E_None ) + { + // iterate over directory content + TStringVector aSubDirs; + ::osl::DirectoryItem aItem; + while ( aDir.getNextItem( aItem ) == ::osl::FileBase::E_None ) + { + ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL ); + if ( aItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None ) + { + if ( aFileStatus.getFileType() == ::osl::FileStatus::Directory ) + aSubDirs.push_back( aFileStatus.getFileURL() ); + else + aResult->push_back( aFileStatus.getFileURL() ); + } + } + + // iterate recursive over subfolders + for (auto const& subDir : aSubDirs) + { + TStringVectorPtr aSubResult = getFiles(subDir); + aResult->insert( aResult->end(), aSubResult->begin(), aSubResult->end() ); + } + } + + return aResult; + } + + + void WordbookMigration::checkAndCreateDirectory( INetURLObject const & rDirURL ) + { + ::osl::FileBase::RC aResult = ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) ); + if ( aResult == ::osl::FileBase::E_NOENT ) + { + INetURLObject aBaseURL( rDirURL ); + aBaseURL.removeSegment(); + checkAndCreateDirectory( aBaseURL ); + ::osl::Directory::create( rDirURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) ); + } + } + +#define MAX_HEADER_LENGTH 16 +static bool IsUserWordbook( const OUString& rFile ) +{ + bool bRet = false; + std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( rFile, StreamMode::STD_READ ); + if ( pStream && !pStream->GetError() ) + { + static const char* const pVerOOo7 = "OOoUserDict1"; + sal_uInt64 const nSniffPos = pStream->Tell(); + static std::size_t nVerOOo7Len = sal::static_int_cast< std::size_t >(strlen( pVerOOo7 )); + char pMagicHeader[MAX_HEADER_LENGTH]; + pMagicHeader[ nVerOOo7Len ] = '\0'; + if (pStream->ReadBytes(static_cast<void *>(pMagicHeader), nVerOOo7Len) == nVerOOo7Len) + { + if ( !strcmp(pMagicHeader, pVerOOo7) ) + bRet = true; + else + { + sal_uInt16 nLen; + pStream->Seek (nSniffPos); + pStream->ReadUInt16( nLen ); + if ( nLen < MAX_HEADER_LENGTH ) + { + pStream->ReadBytes(pMagicHeader, nLen); + pMagicHeader[nLen] = '\0'; + if ( !strcmp(pMagicHeader, "WBSWG2") + || !strcmp(pMagicHeader, "WBSWG5") + || !strcmp(pMagicHeader, "WBSWG6") ) + bRet = true; + } + } + } + } + + return bRet; +} + + + void WordbookMigration::copyFiles() + { + OUString sTargetDir; + ::utl::Bootstrap::PathStatus aStatus = ::utl::Bootstrap::locateUserInstallation( sTargetDir ); + if ( aStatus == ::utl::Bootstrap::PATH_EXISTS ) + { + sTargetDir += "/user/wordbook"; + TStringVectorPtr aFileList = getFiles( m_sSourceDir ); + for (auto const& elem : *aFileList) + { + if (IsUserWordbook(elem) ) + { + std::u16string_view sSourceLocalName = elem.subView( m_sSourceDir.getLength() ); + OUString sTargetName = sTargetDir + sSourceLocalName; + INetURLObject aURL( sTargetName ); + aURL.removeSegment(); + checkAndCreateDirectory( aURL ); + ::osl::FileBase::RC aResult = ::osl::File::copy( elem, sTargetName ); + if ( aResult != ::osl::FileBase::E_None ) + { + SAL_WARN( "desktop", "WordbookMigration::copyFiles: cannot copy " + << elem << " to " << sTargetName); + } + } + } + } + else + { + OSL_FAIL( "WordbookMigration::copyFiles: no user installation!" ); + } + } + + + // XServiceInfo + + + OUString WordbookMigration::getImplementationName() + { + return "com.sun.star.comp.desktop.migration.Wordbooks"; + } + + + sal_Bool WordbookMigration::supportsService(OUString const & ServiceName) + { + return cppu::supportsService(this, ServiceName); + } + + + Sequence< OUString > WordbookMigration::getSupportedServiceNames() + { + return { "com.sun.star.migration.Wordbooks" }; + } + + + // XInitialization + + + void WordbookMigration::initialize( const Sequence< Any >& aArguments ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + const Any* pIter = aArguments.getConstArray(); + const Any* pEnd = pIter + aArguments.getLength(); + for ( ; pIter != pEnd ; ++pIter ) + { + beans::NamedValue aValue; + *pIter >>= aValue; + if ( aValue.Name == "UserData" ) + { + if ( !(aValue.Value >>= m_sSourceDir) ) + { + OSL_FAIL( "WordbookMigration::initialize: argument UserData has wrong type!" ); + } + m_sSourceDir += "/user/wordbook"; + break; + } + } + } + + + // XJob + + + Any WordbookMigration::execute( const Sequence< beans::NamedValue >& ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + copyFiles(); + + return Any(); + } + +} // namespace migration + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +desktop_WordbookMigration_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new migration::WordbookMigration()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/desktop/source/migration/services/wordbookmigration.hxx b/desktop/source/migration/services/wordbookmigration.hxx new file mode 100644 index 000000000..a6388fa42 --- /dev/null +++ b/desktop/source/migration/services/wordbookmigration.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ + +#pragma once + +#include "misc.hxx" +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/task/XJob.hpp> +#include <cppuhelper/implbase.hxx> +#include <osl/mutex.hxx> + + +class INetURLObject; + + +namespace migration +{ + + typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::task::XJob > WordbookMigration_BASE; + + class WordbookMigration : public WordbookMigration_BASE + { + private: + ::osl::Mutex m_aMutex; + OUString m_sSourceDir; + + TStringVectorPtr getFiles( const OUString& rBaseURL ) const; + void checkAndCreateDirectory( INetURLObject const & rDirURL ); + void copyFiles(); + + public: + WordbookMigration(); + virtual ~WordbookMigration() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XJob + virtual css::uno::Any SAL_CALL execute( + const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override; + }; + + +} // namespace migration + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |