/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: migration.cxx,v $ * * $Revision: 1.8 $ * * last change: $Author: vg $ $Date: 2006-04-07 14:46:59 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ #include "migration.hxx" #include "migration_impl.hxx" #include "cfgfilter.hxx" #include <unotools/textsearch.hxx> #include <comphelper/processfactory.hxx> #include <unotools/bootstrap.hxx> #include <rtl/bootstrap.hxx> #include <tools/config.hxx> #include <i18npool/lang.h> #include <tools/urlobj.hxx> #include <osl/file.hxx> #include <osl/mutex.hxx> #include <com/sun/star/lang/XInitialization.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/configuration/backend/XLayer.hpp> #include <com/sun/star/configuration/backend/XSingleLayerStratum.hpp> #include <com/sun/star/util/XRefreshable.hpp> #include <com/sun/star/util/XChangesBatch.hpp> using namespace rtl; using namespace osl; using namespace std; using namespace com::sun::star::uno; 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 namespace com::sun::star::configuration; using namespace com::sun::star::configuration::backend; namespace desktop { static MigrationImpl *pImpl = 0; static Mutex aMutex; static MigrationImpl *getImpl() { MutexGuard aGuard(aMutex); if (pImpl == 0) pImpl = new MigrationImpl(comphelper::getProcessServiceFactory()); return pImpl; } static void releaseImpl() { MutexGuard aGuard(aMutex); if (pImpl != 0) { delete pImpl; pImpl = 0; } } // static main entry point for the migration process void Migration::doMigration() { sal_Bool bResult = sal_False; try { bResult = getImpl()->doMigration(); } catch (Exception& e) { OString aMsg("doMigration() exception: "); aMsg += OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMsg.getStr()); } OSL_ENSURE(bResult, "Migration has not been successfull"); // shut down migration framework releaseImpl(); } void Migration::cancelMigration() { releaseImpl(); } sal_Bool Migration::checkMigration() { return getImpl()->checkMigration(); } OUString Migration::getOldVersionName() { return getImpl()->getOldVersionName(); } OUString MigrationImpl::getOldVersionName() { return m_aInfo.productname; } sal_Bool MigrationImpl::checkMigration() { if (m_aInfo.userdata.getLength() > 0 && ! checkMigrationCompleted()) return sal_True; else return sal_False; } MigrationImpl::MigrationImpl(const Reference< XMultiServiceFactory >& xFactory) : m_xFactory(xFactory) , m_vrVersions(new strings_v) , m_vrMigrations(readMigrationSteps()) , m_aInfo(findInstallation()) , m_vrFileList(compileFileList()) , m_vrServiceList(compileServiceList()) { } MigrationImpl::~MigrationImpl() { } sal_Bool MigrationImpl::doMigration() { sal_Bool result = sal_False; try{ copyFiles(); // execute the migration items from Setup.xcu // and refresh the cache copyConfig(); refresh(); // execute custom migration services from Setup.xcu // and refresh the cache runServices(); refresh(); result = sal_True; } catch (...) { OString aMsg("An unexpected exception was thrown during migration"); aMsg += "\nOldVersion: " + OUStringToOString(m_aInfo.productname, RTL_TEXTENCODING_ASCII_US); aMsg += "\nDataPath : " + OUStringToOString(m_aInfo.userdata, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMsg.getStr()); } // prevent running the migration multiple times setMigrationCompleted(); return sal_False; } void MigrationImpl::refresh() { Reference< XRefreshable > xRefresh(m_xFactory->createInstance( OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider")), UNO_QUERY); if (xRefresh.is()) xRefresh->refresh(); else OSL_ENSURE(sal_False, "could not get XRefresh interface from default config provider. No refresh done."); } void MigrationImpl::setMigrationCompleted() { try { Reference< XPropertySet > aPropertySet(getConfigAccess("org.openoffice.Setup/Office", true), UNO_QUERY_THROW); aPropertySet->setPropertyValue(OUString::createFromAscii("MigrationCompleted"), makeAny(sal_True)); Reference< XChangesBatch >(aPropertySet, UNO_QUERY_THROW)->commitChanges(); } catch (...) { // fail silently } } sal_Bool MigrationImpl::checkMigrationCompleted() { sal_Bool bMigrationCompleted = sal_False; try { Reference< XPropertySet > aPropertySet( getConfigAccess("org.openoffice.Setup/Office"), UNO_QUERY_THROW); aPropertySet->getPropertyValue( OUString::createFromAscii("MigrationCompleted")) >>= bMigrationCompleted; } catch (Exception& e) { // just return false... } return bMigrationCompleted; } migrations_vr MigrationImpl::readMigrationSteps() { // get supported version names Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration"), UNO_QUERY_THROW); Sequence< OUString > seqVersions; aMigrationAccess->getByName(OUString::createFromAscii("SupportedVersions")) >>= seqVersions; for (sal_Int32 i=0; i<seqVersions.getLength(); i++) m_vrVersions->push_back(seqVersions[i].trim()); // get migration description from from org.openoffice.Setup/Migration // and build vector of migration steps Reference< XNameAccess > theNameAccess(getConfigAccess("org.openoffice.Setup/Migration/MigrationSteps"), UNO_QUERY_THROW); Sequence< OUString > seqMigrations = theNameAccess->getElementNames(); Reference< XNameAccess > tmpAccess; Reference< XNameAccess > tmpAccess2; Sequence< OUString > tmpSeq; migrations_vr vrMigrations(new migrations_v); for (sal_Int32 i = 0; i < seqMigrations.getLength(); i++) { // get current migration step theNameAccess->getByName(seqMigrations[i]) >>= tmpAccess; // tmpStepPtr = new migration_step(); migration_step tmpStep; tmpStep.name = seqMigrations[i]; // read included files from current step description if (tmpAccess->getByName(OUString::createFromAscii("IncludedFiles")) >>= tmpSeq) { for (sal_Int32 j=0; j<tmpSeq.getLength(); j++) tmpStep.includeFiles.push_back(tmpSeq[j]); } // exluded files... if (tmpAccess->getByName(OUString::createFromAscii("ExcludedFiles")) >>= tmpSeq) { for (sal_Int32 j=0; j<tmpSeq.getLength(); j++) tmpStep.excludeFiles.push_back(tmpSeq[j]); } // included nodes... if (tmpAccess->getByName(OUString::createFromAscii("IncludedNodes")) >>= tmpSeq) { for (sal_Int32 j=0; j<tmpSeq.getLength(); j++) tmpStep.includeConfig.push_back(tmpSeq[j]); } // excluded nodes... if (tmpAccess->getByName(OUString::createFromAscii("ExcludedNodes")) >>= tmpSeq) { for (sal_Int32 j=0; j<tmpSeq.getLength(); j++) tmpStep.excludeConfig.push_back(tmpSeq[j]); } // config components if (tmpAccess->getByName(OUString::createFromAscii("ServiceConfigComponents")) >>= tmpSeq) { for (sal_Int32 j=0; j<tmpSeq.getLength(); j++) tmpStep.configComponents.push_back(tmpSeq[j]); } // generic service tmpAccess->getByName(OUString::createFromAscii("MigrationService")) >>= tmpStep.service; vrMigrations->push_back(tmpStep); } return vrMigrations; } install_info MigrationImpl::findInstallation() { OUString usVersion; install_info aInfo; utl::Bootstrap::PathStatus aStatus; aStatus = utl::Bootstrap::locateVersionFile(usVersion); if (aStatus != utl::Bootstrap::PATH_EXISTS) { // aStatus = utl::Bootstrap::locateUserInstallation(usVersion); // if (aStatus == utl::Bootstrap::PATH_EXISTS) if (rtl::Bootstrap::get( OUString::createFromAscii("SYSUSERCONFIG"), usVersion)) { aStatus = utl::Bootstrap::PATH_EXISTS; #ifdef UNX usVersion += OUString::createFromAscii("/.sversionrc"); #else usVersion += OUString::createFromAscii("/sversion.ini"); #endif } else return aInfo; } Config aVersion(usVersion); aVersion.SetGroup("Versions"); strings_v vInst; ByteString sInst; for (int i=0; i<aVersion.GetKeyCount(); i++) { sInst =aVersion.GetKeyName(i); vInst.push_back(OUString(static_cast< OString >(sInst), sInst.Len(), RTL_TEXTENCODING_UTF8)); } ByteString sInstall; strings_v::const_iterator i_ins = vInst.begin(); while (i_ins != vInst.end()) { strings_v::const_iterator i_ver = m_vrVersions->begin(); while (i_ver != m_vrVersions->end()) { // this will use the last valid version from the sversion file // aInfo will be overwritten for each matching version identifier if ( i_ins->indexOf(*i_ver) == 0) { sInstall = aVersion.ReadKey(OUStringToOString(*i_ins, RTL_TEXTENCODING_UTF8)); aInfo.productname = *i_ins; aInfo.userdata = OUString(static_cast< OString >(sInstall), sInstall.Len(), RTL_TEXTENCODING_UTF8); } i_ver++; } i_ins++; } return aInfo; } strings_vr MigrationImpl::applyPatterns(const strings_v& vSet, const strings_v& vPatterns) const { using namespace utl; strings_vr vrResult(new strings_v); strings_v::const_iterator i_set; strings_v::const_iterator i_pat = vPatterns.begin(); while (i_pat != vPatterns.end()) { // find matches for this pattern in input set // and copy them to the result SearchParam param(*i_pat, SearchParam::SRCH_REGEXP); TextSearch ts(param, LANGUAGE_DONTKNOW); i_set = vSet.begin(); xub_StrLen start = 0; xub_StrLen end = 0; while (i_set != vSet.end()) { end = (xub_StrLen)(i_set->getLength()); if (ts.SearchFrwrd(*i_set, &start, &end)) vrResult->push_back(*i_set); i_set++; } i_pat++; } return vrResult; } strings_vr MigrationImpl::getAllFiles(const OUString& baseURL) const { using namespace osl; 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(FileStatusMask_Type | FileStatusMask_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 strings_v::const_iterator i = vSubDirs.begin(); while (i != vSubDirs.end()) { vrSubResult = getAllFiles(*i); vrResult->insert(vrResult->end(), vrSubResult->begin(), vrSubResult->end()); i++; } } return vrResult; } strings_vr MigrationImpl::compileFileList() { strings_vr vrResult(new strings_v); strings_vr vrInclude; strings_vr vrExclude; strings_vr vrTemp; // get a list of all files: strings_vr vrFiles = getAllFiles(m_aInfo.userdata); // get a file list result for each migration step migrations_v::const_iterator i_migr = m_vrMigrations->begin(); while (i_migr != m_vrMigrations->end()) { vrInclude = applyPatterns(*vrFiles, i_migr->includeFiles); vrExclude = applyPatterns(*vrFiles, i_migr->excludeFiles); substract(*vrInclude, *vrExclude); vrResult->insert(vrResult->end(), vrInclude->begin(), vrInclude->end()); i_migr++; } return vrResult; } void MigrationImpl::copyConfig() { try { // 1. get a list of all components from hierachy browser Reference< XJob > xBrowser(m_xFactory->createInstance( OUString::createFromAscii("com.sun.star.configuration.backend.LocalHierarchyBrowser")), UNO_QUERY_THROW); Sequence< NamedValue > seqArgs(2); seqArgs[0] = NamedValue( OUString::createFromAscii("LayerDataUrl"), makeAny(m_aInfo.userdata + OUString::createFromAscii("/user/registry"))); seqArgs[1] = NamedValue( OUString::createFromAscii("FetchComponentNames"), makeAny(sal_True)); // execute the search Any aResult = xBrowser->execute(seqArgs); Sequence< OUString > seqComponents; aResult >>= seqComponents; OSL_ENSURE(seqComponents.getLength()>0, "MigrationImpl::copyConfig(): no config components available"); // 2. create an importer Reference< XJob > xImporter(m_xFactory->createInstance( OUString::createFromAscii("com.sun.star.configuration.backend.LocalDataImporter")), UNO_QUERY_THROW); // 3. for each migration step... Sequence< NamedValue > importerArgs(3); importerArgs[0] = NamedValue( OUString::createFromAscii("LayerDataUrl"), makeAny(m_aInfo.userdata + OUString::createFromAscii("/user/registry"))); importerArgs[1] = NamedValue( OUString::createFromAscii("LayerFilter"), Any()); importerArgs[2] = NamedValue( OUString::createFromAscii("Component"), Any()); migrations_v::const_iterator i_mig = m_vrMigrations->begin(); while (i_mig != m_vrMigrations->end()) { // a. create config filter for step Reference< XInitialization > xFilter( new CConfigFilter(&(i_mig->includeConfig), &(i_mig->excludeConfig))); importerArgs[1].Value = makeAny(xFilter); // b. run each importer with config filter for (sal_Int32 i=0; i<seqComponents.getLength(); i++) { OUString component = seqComponents[i]; importerArgs[2].Value = makeAny(seqComponents[i]); try { Any aResult = xImporter->execute(importerArgs); Exception myException; if (aResult >>= myException) throw myException; } catch(Exception& aException) { OString aMsg("Exception in config layer import.\ncomponent: "); aMsg += OUStringToOString(seqComponents[i], RTL_TEXTENCODING_ASCII_US); aMsg += "\nmessage: "; aMsg += OUStringToOString(aException.Message, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMsg.getStr()); } } i_mig++; } } catch (Exception& e) { OString aMsg("Exception in config layer import.\nmessage: "); aMsg += OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMsg.getStr()); } } // removes elements of vector 2 in vector 1 void MigrationImpl::substract(strings_v& va, const strings_v& vb_c) const { strings_v vb(vb_c); // ensure uniqueness of entries sort(va.begin(), va.end()); sort(vb.begin(), vb.end()); unique(va.begin(), va.end()); unique(vb.begin(), vb.end()); strings_v::const_iterator i_ex = vb.begin(); strings_v::iterator i_in; strings_v::iterator i_next; while (i_ex != vb.end()) { i_in = va.begin(); while (i_in != va.end()) { if ( *i_in == *i_ex) { i_next = i_in+1; va.erase(i_in); i_in = i_next; // we can only find one match since we // ensured uniquness of the entries. ergo: break; } else i_in++; } i_ex++; } } Reference< XNameAccess > MigrationImpl::getConfigAccess(const sal_Char* pPath, sal_Bool bUpdate) { Reference< XNameAccess > xNameAccess; try{ OUString sConfigSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider"); OUString sAccessSrvc; if (bUpdate) sAccessSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationUpdateAccess"); else sAccessSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationAccess"); OUString sConfigURL = OUString::createFromAscii(pPath); // get configuration provider Reference< XMultiServiceFactory > theMSF = comphelper::getProcessServiceFactory(); Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory > ( theMSF->createInstance( sConfigSrvc ),UNO_QUERY_THROW ); // access the provider Sequence< Any > theArgs(1); theArgs[ 0 ] <<= sConfigURL; xNameAccess = Reference< XNameAccess > ( theConfigProvider->createInstanceWithArguments( sAccessSrvc, theArgs ), UNO_QUERY_THROW ); } catch (com::sun::star::uno::Exception& e) { OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMsg.getStr()); } return xNameAccess; } static FileBase::RC _checkAndCreateDirectory(INetURLObject& dirURL) { FileBase::RC result = Directory::create(dirURL.GetMainURL(INetURLObject::DECODE_TO_IURI)); if (result == FileBase::E_NOENT) { INetURLObject baseURL(dirURL); baseURL.removeSegment(); _checkAndCreateDirectory(baseURL); return Directory::create(dirURL.GetMainURL(INetURLObject::DECODE_TO_IURI)); } else return result; } void MigrationImpl::copyFiles() { strings_v::const_iterator i_file = m_vrFileList->begin(); OUString localName; OUString destName; OUString userInstall; utl::Bootstrap::PathStatus aStatus; aStatus = utl::Bootstrap::locateUserInstallation(userInstall); if (aStatus == utl::Bootstrap::PATH_EXISTS) { while (i_file != m_vrFileList->end()) { // remove installation prefix from file localName = i_file->copy(m_aInfo.userdata.getLength()); destName = userInstall + localName; INetURLObject aURL(destName); // check whether destination directory exists aURL.removeSegment(); _checkAndCreateDirectory(aURL); FileBase::RC copyResult = File::copy(*i_file, destName); if (copyResult != FileBase::E_None) { OString msg("Cannot copy "); msg += OUStringToOString(*i_file, RTL_TEXTENCODING_UTF8) + " to " + OUStringToOString(destName, RTL_TEXTENCODING_UTF8); OSL_ENSURE(sal_False, msg.getStr()); } i_file++; } } else { OSL_ENSURE(sal_False, "copyFiles: UserInstall does not exist"); } } void MigrationImpl::runServices() { //create stratum for old user layer OUString aOldLayerURL = m_aInfo.userdata; aOldLayerURL += OUString::createFromAscii("/user/registry"); OUString aStratumSvc = OUString::createFromAscii("com.sun.star.configuration.backend.LocalSingleStratum"); Sequence< Any > stratumArgs(1); stratumArgs[0] = makeAny(aOldLayerURL); Reference< XSingleLayerStratum> xStartum( m_xFactory->createInstanceWithArguments( aStratumSvc, stratumArgs), UNO_QUERY); // Build argument array Sequence< Any > seqArguments(3); seqArguments[0] = makeAny(NamedValue( OUString::createFromAscii("Productname"), makeAny(m_aInfo.productname))); seqArguments[1] = makeAny(NamedValue( OUString::createFromAscii("UserData"), makeAny(m_aInfo.userdata))); // create an instance of every migration service // and execute the migration job Reference< XJob > xMigrationJob; migrations_v::const_iterator i_mig = m_vrMigrations->begin(); while (i_mig != m_vrMigrations->end()) { if( i_mig->service.getLength() > 0) { try { // create access to old configuration components in the user layer // that were requested by the migration service Sequence< NamedValue > seqComponents(i_mig->configComponents.size()); strings_v::const_iterator i_comp = i_mig->configComponents.begin(); sal_Int32 i = 0; while (i_comp != i_mig->configComponents.end() && xStartum.is()) { // create Layer for i_comp seqComponents[i] = NamedValue( *i_comp, makeAny(xStartum->getLayer(*i_comp, OUString()))); // next component i_comp++; i++; } // set old config argument seqArguments[2] = makeAny(NamedValue( OUString::createFromAscii("OldConfiguration"), makeAny(seqComponents))); xMigrationJob = Reference< XJob >(m_xFactory->createInstanceWithArguments( i_mig->service, seqArguments), UNO_QUERY_THROW); xMigrationJob->execute(Sequence< NamedValue >()); } catch (Exception& e) { OString aMsg("Execution of migration service failed (Exception caught).\nService: "); aMsg += OUStringToOString(i_mig->service, RTL_TEXTENCODING_ASCII_US) + "\nMessage: "; aMsg += OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMsg.getStr()); } catch (...) { OString aMsg("Execution of migration service failed (Exception caught).\nService: "); aMsg += OUStringToOString(i_mig->service, RTL_TEXTENCODING_ASCII_US) + "\nNo message available"; OSL_ENSURE(sal_False, aMsg.getStr()); } } i_mig++; } } strings_vr MigrationImpl::compileServiceList() { strings_vr vrResult(new strings_v); migrations_v::const_iterator i_migr = m_vrMigrations->begin(); while (i_migr != m_vrMigrations->end()) { vrResult->push_back(i_migr->service); i_migr++; } return vrResult; } } // namespace desktop