diff options
Diffstat (limited to 'desktop/source/deployment/manager/dp_manager.cxx')
-rw-r--r-- | desktop/source/deployment/manager/dp_manager.cxx | 1689 |
1 files changed, 1689 insertions, 0 deletions
diff --git a/desktop/source/deployment/manager/dp_manager.cxx b/desktop/source/deployment/manager/dp_manager.cxx new file mode 100644 index 000000000000..680dbf855d9d --- /dev/null +++ b/desktop/source/deployment/manager/dp_manager.cxx @@ -0,0 +1,1689 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_desktop.hxx" + +#include "dp_ucb.h" +#include "dp_resource.h" +#include "dp_platform.hxx" +#include "dp_manager.h" +#include "dp_identifier.hxx" +#include "rtl/ustrbuf.hxx" +#include "rtl/string.hxx" +#include "rtl/uri.hxx" +#include "rtl/bootstrap.hxx" +#include "osl/diagnose.h" +#include "osl/file.hxx" +#include "osl/security.hxx" +#include "cppuhelper/weakref.hxx" +#include "cppuhelper/exc_hlp.hxx" +#include "cppuhelper/implbase1.hxx" +#include "cppuhelper/interfacecontainer.hxx" +#include "comphelper/servicedecl.hxx" +#include "comphelper/sequence.hxx" +#include "xmlscript/xml_helper.hxx" +#include "svl/inettype.hxx" +#include "com/sun/star/lang/DisposedException.hpp" +#include "com/sun/star/lang/WrappedTargetRuntimeException.hpp" +#include "com/sun/star/beans/UnknownPropertyException.hpp" +#include "com/sun/star/util/XUpdatable.hpp" +#include "com/sun/star/sdbc/XResultSet.hpp" +#include "com/sun/star/sdbc/XRow.hpp" +#include "com/sun/star/ucb/XContentAccess.hpp" +#include "com/sun/star/ucb/NameClash.hpp" +#include "com/sun/star/deployment/VersionException.hpp" +#include "com/sun/star/deployment/InstallException.hpp" +#include "com/sun/star/deployment/Prerequisites.hpp" +#include "com/sun/star/task/XInteractionApprove.hpp" +#include "com/sun/star/ucb/UnsupportedCommandException.hpp" +#include "boost/bind.hpp" +#include "tools/urlobj.hxx" +#include "unotools/tempfile.hxx" + +#include "osl/file.hxx" +#include <vector> +#include <list> +#include "dp_descriptioninfoset.hxx" +#include "dp_commandenvironments.hxx" +#include "dp_properties.hxx" + +using namespace ::dp_misc; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using ::rtl::OUString; + +namespace dp_log { +extern comphelper::service_decl::ServiceDecl const serviceDecl; +} + +namespace dp_registry { +Reference<deployment::XPackageRegistry> create( + OUString const & context, + OUString const & cachePath, bool readOnly, + Reference<XComponentContext> const & xComponentContext ); +} + +namespace dp_manager { + +struct MatchTempDir +{ + OUString m_str; + MatchTempDir( OUString const & str ) : m_str( str ) {} + bool operator () ( ActivePackages::Entries::value_type const & v ) const { + return v.second.temporaryName.equalsIgnoreAsciiCase( m_str ); + } +}; + + +namespace { +OUString getExtensionFolder(OUString const & parentFolder, + Reference<ucb::XCommandEnvironment> const & xCmdEnv) +{ + ::ucbhelper::Content tempFolder( + parentFolder, xCmdEnv ); + Reference<sdbc::XResultSet> xResultSet( + tempFolder.createCursor( + Sequence<OUString>( &StrTitle::get(), 1 ), + ::ucbhelper::INCLUDE_FOLDERS_ONLY ) ); + + OUString title; + while (xResultSet->next()) + { + title = Reference<sdbc::XRow>( + xResultSet, UNO_QUERY_THROW )->getString(1 /* Title */ ) ; + break; + } + return title; +} +} +//______________________________________________________________________________ +void PackageManagerImpl::initActivationLayer( + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + if (m_activePackages.getLength() == 0) + { + OSL_ASSERT( m_registryCache.getLength() == 0 ); + // documents temp activation: + m_activePackagesDB.reset( new ActivePackages ); + ::ucbhelper::Content ucbContent; + if (create_ucb_content( &ucbContent, m_context, xCmdEnv, + false /* no throw */ )) + { + // scan for all entries in m_packagesDir: + Reference<sdbc::XResultSet> xResultSet( + ucbContent.createCursor( + Sequence<OUString>( &StrTitle::get(), 1 ), + ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) ); + while (xResultSet->next()) + { + Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW ); + OUString title( xRow->getString( 1 /* Title */ ) ); + // xxx todo: remove workaround for tdoc + if (title.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( + "this_is_a_dummy_stream_just_there_" + "as_a_workaround_for_a_" + "temporary_limitation_of_the_" + "storage_api_implementation") )) + continue; + if (title.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( + "META-INF") ) ) + continue; + + ::ucbhelper::Content sourceContent( + Reference<XContentAccess>( + xResultSet, UNO_QUERY_THROW )->queryContent(), + xCmdEnv ); + + OUString mediaType( detectMediaType( sourceContent, + false /* no throw */) ); + if (mediaType.getLength() >0) + { + ActivePackages::Data dbData; + insertToActivationLayer( + Sequence<css::beans::NamedValue>(),mediaType, sourceContent, + title, &dbData ); + + insertToActivationLayerDB( title, dbData ); + //TODO #i73136#: insertToActivationLayerDB needs id not + // title, but the whole m_activePackages.getLength()==0 + // case (i.e., document-relative deployment) currently + // does not work, anyway. + } + } + } + } + else + { + // user|share: + OSL_ASSERT( m_activePackages.getLength() > 0 ); + m_activePackages_expanded = expandUnoRcUrl( m_activePackages ); + m_registrationData_expanded = expandUnoRcUrl(m_registrationData); + if (!m_readOnly) + create_folder( 0, m_activePackages_expanded, xCmdEnv, true); + + OUString dbName; + if (m_context.equals(OUSTR("user"))) + dbName = m_activePackages_expanded + OUSTR(".db"); + else + { + //Create the extension data base in the user installation + create_folder( 0, m_registrationData_expanded, xCmdEnv, true); + dbName = m_registrationData_expanded + OUSTR("/extensions.db"); + } + //The data base can always be written because it it always in the user installation + m_activePackagesDB.reset( + new ActivePackages( dbName, false ) ); + + if (! m_readOnly && ! m_context.equals(OUSTR("bundled"))) + { + // clean up activation layer, scan for zombie temp dirs: + ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() ); + + ::ucbhelper::Content tempFolder( + m_activePackages_expanded, xCmdEnv ); + Reference<sdbc::XResultSet> xResultSet( + tempFolder.createCursor( + Sequence<OUString>( &StrTitle::get(), 1 ), + ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) ); + // get all temp directories: + ::std::vector<OUString> tempEntries; + ::std::vector<OUString> removedEntries; + while (xResultSet->next()) + { + OUString title( + Reference<sdbc::XRow>( + xResultSet, UNO_QUERY_THROW )->getString( + 1 /* Title */ ) ); + + const char extensionRemoved[] = "removed"; + if (title.endsWithAsciiL( + extensionRemoved, sizeof(extensionRemoved) - 1)) + { + //save the file name withouth the "removed" part + sal_Int32 index = title.lastIndexOfAsciiL( + extensionRemoved, sizeof(extensionRemoved) - 1); + OUString remFile = title.copy(0, index); + removedEntries.push_back(::rtl::Uri::encode( + remFile, rtl_UriCharClassPchar, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ) ); + } + else + { + tempEntries.push_back( ::rtl::Uri::encode( + title, rtl_UriCharClassPchar, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ) ); + } + } + + bool bShared = m_context.equals(OUSTR("shared")) ? true : false; + for ( ::std::size_t pos = 0; pos < tempEntries.size(); ++pos ) + { + OUString const & tempEntry = tempEntries[ pos ]; + const MatchTempDir match( tempEntry ); + if (::std::find_if( id2temp.begin(), id2temp.end(), match ) == + id2temp.end()) + { + const OUString url( + makeURL(m_activePackages_expanded, tempEntry ) ); + + //In case of shared extensions, new entries are regarded as + //added extensions if there is no xxx.tmpremoved file. + if (bShared) + { + if (::std::find(removedEntries.begin(), removedEntries.end(), tempEntry) == + removedEntries.end()) + { + continue; + } + else + { + //Make sure only the same user removes the extension, who + //previously unregistered it. This is avoid races if multiple instances + //of OOo are running which all have write access to the shared installation. + //For example, a user removes the extension, but keeps OOo + //running. Parts of the extension may still be loaded and used by OOo. + //Therefore the extension is only deleted the next time the extension manager is + //run after restarting OOo. While OOo is still running, another user starts OOo + //which would deleted the extension files. If the same user starts another + //instance of OOo then the lock file will prevent this. + OUString aUserName; + ::osl::Security aSecurity; + aSecurity.getUserName( aUserName ); + ucbhelper::Content remFileContent( + url + OUSTR("removed"), Reference<XCommandEnvironment>()); + ::rtl::ByteSequence data = dp_misc::readFile(remFileContent); + ::rtl::OString osData(reinterpret_cast<const sal_Char*>(data.getConstArray()), + data.getLength()); + OUString sData = ::rtl::OStringToOUString( + osData, RTL_TEXTENCODING_UTF8); + if (!sData.equals(aUserName)) + continue; + } + } + // temp entry not needed anymore: + erase_path( url + OUSTR("_"), + Reference<XCommandEnvironment>(), + false /* no throw: ignore errors */ ); + erase_path( url, Reference<XCommandEnvironment>(), + false /* no throw: ignore errors */ ); + //delete the xxx.tmpremoved file + erase_path(url + OUSTR("removed"), + Reference<XCommandEnvironment>(), false); + } + } + } + } +} + +//______________________________________________________________________________ +void PackageManagerImpl::initRegistryBackends() +{ + if (m_registryCache.getLength() > 0) + create_folder( 0, m_registryCache, + Reference<XCommandEnvironment>(), false); + m_xRegistry.set( ::dp_registry::create( + m_context, m_registryCache, false, + m_xComponentContext ) ); +} + +//______________________________________________________________________________ +Reference<deployment::XPackageManager> PackageManagerImpl::create( + Reference<XComponentContext> const & xComponentContext, + OUString const & context ) +{ + PackageManagerImpl * that = new PackageManagerImpl( + xComponentContext, context ); + Reference<deployment::XPackageManager> xPackageManager( that ); + + OUString packages, logFile, stampURL; + if (context.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("user") )) { + that->m_activePackages = OUSTR( + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages"); + that->m_registrationData = OUSTR( + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE"); + that->m_registryCache = OUSTR( + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/registry"); + logFile = OUSTR( + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/log.txt"); + //We use the extension .sys for the file because on Windows Vista a sys + //(as well as exe and dll) file + //will not be written in the VirtualStore. For example if the process has no + //admin right once cannot write to the %programfiles% folder. However, when + //virtualization is used, the file will be written into the VirtualStore and + //it appears as if one could write to %programfiles%. When we test for write + //access to the office/shared folder for shared extensions then this typically + //fails because a normal user typically cannot write to this folder. However, + //using virtualization it appears that he/she can. Then a shared extension can + //be installed but is only visible for the user (because the extension is in + //the virtual store). + stampURL = OUSTR( + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/stamp.sys"); + } + else if (context.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("shared") )) { + that->m_activePackages = OUSTR( + "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages"); + that->m_registrationData = OUSTR( + "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER"); + that->m_registryCache = OUSTR( + "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/registry"); + logFile = OUSTR( + "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/log.txt"); + stampURL = OUSTR( + "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/stamp.sys"); + } + else if (context.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("bundled") )) { + that->m_activePackages = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS"); + that->m_registrationData = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER"); + that->m_registryCache = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/registry"); + logFile = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/log.txt"); + //No stamp file. We assume that bundled is always readonly. It must not be + //modified from ExtensionManager but only by the installer + } + else if (context.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("bundled_prereg") )) { + //This is a bundled repository but the registration data + //is in the brand layer: share/prereg + //It is special because the registration data are copied at the first startup + //into the user installation. The processed help and xcu files are not + //copied. Instead the backenddb.xml for the help backend references the help + //by using $BUNDLED_EXTENSION_PREREG instead $BUNDLED_EXTENSIONS_USER. The + //configmgr.ini also used $BUNDLED_EXTENSIONS_PREREG to refer to the xcu file + //which contain the replacement for %origin%. + that->m_activePackages = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS"); + that->m_registrationData = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_PREREG"); + that->m_registryCache = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_PREREG/registry"); + logFile = OUSTR( + "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_PREREG/log.txt"); + } + else if (context.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("tmp") )) { + that->m_activePackages = OUSTR( + "vnd.sun.star.expand:$TMP_EXTENSIONS/extensions"); + that->m_registrationData = OUSTR( + "vnd.sun.star.expand:$TMP_EXTENSIONS"); + that->m_registryCache = OUSTR( + "vnd.sun.star.expand:$TMP_EXTENSIONS/registry"); + stampURL = OUSTR( + "vnd.sun.star.expand:$TMP_EXTENSIONS/stamp.sys"); + } + else if (! context.matchAsciiL( + RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.tdoc:/") )) { + throw lang::IllegalArgumentException( + OUSTR("invalid context given: ") + context, + Reference<XInterface>(), static_cast<sal_Int16>(-1) ); + } + + Reference<XCommandEnvironment> xCmdEnv; + + try { + //There is no stampURL for the bundled folder + if (stampURL.getLength() > 0) + { +#define CURRENT_STAMP "1" + try { + //The osl file API does not allow to find out if one can write + //into a folder. Therefore we try to write a file. Then we delete + //it, so that it does not hinder uninstallation of OOo + // probe writing: + ::ucbhelper::Content ucbStamp( stampURL, xCmdEnv ); + ::rtl::OString stamp( + RTL_CONSTASCII_STRINGPARAM(CURRENT_STAMP) ); + Reference<io::XInputStream> xData( + ::xmlscript::createInputStream( + ::rtl::ByteSequence( + reinterpret_cast<sal_Int8 const *>(stamp.getStr()), + stamp.getLength() ) ) ); + ucbStamp.writeStream( xData, true /* replace existing */ ); + that->m_readOnly = false; + erase_path( stampURL, xCmdEnv ); + } + catch (RuntimeException &) { + try { + erase_path( stampURL, xCmdEnv ); + } catch (...) + { + } + throw; + } + catch (Exception &) { + that->m_readOnly = true; + } + } + + if (!that->m_readOnly && logFile.getLength() > 0) + { + const Any any_logFile(logFile); + that->m_xLogFile.set( + that->m_xComponentContext->getServiceManager() + ->createInstanceWithArgumentsAndContext( + dp_log::serviceDecl.getSupportedServiceNames()[0], + Sequence<Any>( &any_logFile, 1 ), + that->m_xComponentContext ), + UNO_QUERY_THROW ); + xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv, that->m_xLogFile ) ); + } + + that->initRegistryBackends(); + that->initActivationLayer( xCmdEnv ); + + return xPackageManager; + + } + catch (RuntimeException &) { + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + ::rtl::OUStringBuffer buf; + buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("[context=\"") ); + buf.append( context ); + buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( + "\"] caught unexpected exception!") ); + throw lang::WrappedTargetRuntimeException( + buf.makeStringAndClear(), Reference<XInterface>(), exc ); + } +} + +//______________________________________________________________________________ +PackageManagerImpl::~PackageManagerImpl() +{ +} + +//______________________________________________________________________________ +void PackageManagerImpl::fireModified() +{ + ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer( + util::XModifyListener::static_type() ); + if (pContainer != 0) { + pContainer->forEach<util::XModifyListener>( + boost::bind(&util::XModifyListener::modified, _1, + lang::EventObject(static_cast<OWeakObject *>(this))) ); + } +} + +//______________________________________________________________________________ +void PackageManagerImpl::disposing() +{ + try { +// // xxx todo: guarding? +// ::osl::MutexGuard guard( getMutex() ); + try_dispose( m_xLogFile ); + m_xLogFile.clear(); + try_dispose( m_xRegistry ); + m_xRegistry.clear(); + m_activePackagesDB.reset(0); + m_xComponentContext.clear(); + + t_pm_helper::disposing(); + + } + catch (RuntimeException &) { + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + throw lang::WrappedTargetRuntimeException( + OUSTR("caught unexpected exception while disposing..."), + static_cast<OWeakObject *>(this), exc ); + } +} + +// XComponent +//______________________________________________________________________________ +void PackageManagerImpl::dispose() throw (RuntimeException) +{ + check(); + WeakComponentImplHelperBase::dispose(); +} + +//______________________________________________________________________________ +void PackageManagerImpl::addEventListener( + Reference<lang::XEventListener> const & xListener ) throw (RuntimeException) +{ + check(); + WeakComponentImplHelperBase::addEventListener( xListener ); +} + +//______________________________________________________________________________ +void PackageManagerImpl::removeEventListener( + Reference<lang::XEventListener> const & xListener ) throw (RuntimeException) +{ + check(); + WeakComponentImplHelperBase::removeEventListener( xListener ); +} + +// XPackageManager +//______________________________________________________________________________ +OUString PackageManagerImpl::getContext() throw (RuntimeException) +{ + check(); + return m_context; +} + +//______________________________________________________________________________ +Sequence< Reference<deployment::XPackageTypeInfo> > +PackageManagerImpl::getSupportedPackageTypes() throw (RuntimeException) +{ + OSL_ASSERT( m_xRegistry.is() ); + return m_xRegistry->getSupportedPackageTypes(); +} + +//______________________________________________________________________________ +Reference<task::XAbortChannel> PackageManagerImpl::createAbortChannel() + throw (RuntimeException) +{ + check(); + return new AbortChannel; +} + +// XModifyBroadcaster +//______________________________________________________________________________ +void PackageManagerImpl::addModifyListener( + Reference<util::XModifyListener> const & xListener ) + throw (RuntimeException) +{ + check(); + rBHelper.addListener( ::getCppuType( &xListener ), xListener ); +} + +//______________________________________________________________________________ +void PackageManagerImpl::removeModifyListener( + Reference<util::XModifyListener> const & xListener ) + throw (RuntimeException) +{ + check(); + rBHelper.removeListener( ::getCppuType( &xListener ), xListener ); +} + +//______________________________________________________________________________ +OUString PackageManagerImpl::detectMediaType( + ::ucbhelper::Content const & ucbContent_, bool throw_exc ) +{ + ::ucbhelper::Content ucbContent(ucbContent_); + OUString url( ucbContent.getURL() ); + OUString mediaType; + if (url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.tdoc:") ) || + url.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.pkg:") )) + { + try { + ucbContent.getPropertyValue( OUSTR("MediaType") ) >>= mediaType; + } + catch (beans::UnknownPropertyException &) { + } + OSL_ENSURE( mediaType.getLength() > 0, "### no media-type?!" ); + } + if (mediaType.getLength() == 0) + { + try { + Reference<deployment::XPackage> xPackage( + m_xRegistry->bindPackage( + url, OUString(), false, OUString(), ucbContent.getCommandEnvironment() ) ); + const Reference<deployment::XPackageTypeInfo> xPackageType( + xPackage->getPackageType() ); + OSL_ASSERT( xPackageType.is() ); + if (xPackageType.is()) + mediaType = xPackageType->getMediaType(); + } + catch (lang::IllegalArgumentException & exc) { + if (throw_exc) + throw; + (void) exc; + OSL_FAIL( ::rtl::OUStringToOString( + exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + return mediaType; +} + +//______________________________________________________________________________ +OUString PackageManagerImpl::insertToActivationLayer( + Sequence<beans::NamedValue> const & properties, + OUString const & mediaType, ::ucbhelper::Content const & sourceContent_, + OUString const & title, ActivePackages::Data * dbData ) +{ + ::ucbhelper::Content sourceContent(sourceContent_); + Reference<XCommandEnvironment> xCmdEnv( + sourceContent.getCommandEnvironment() ); + + String baseDir(m_activePackages_expanded); + ::utl::TempFile aTemp(&baseDir, sal_False); + OUString tempEntry = aTemp.GetURL(); + tempEntry = tempEntry.copy(tempEntry.lastIndexOf('/') + 1); + OUString destFolder = makeURL( m_activePackages, tempEntry); + destFolder += OUSTR("_"); + + // prepare activation folder: + ::ucbhelper::Content destFolderContent; + create_folder( &destFolderContent, destFolder, xCmdEnv ); + + // copy content into activation temp dir: + if (mediaType.matchIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM( + "application/vnd.sun.star.package-bundle") ) || + // xxx todo: more sophisticated parsing + mediaType.matchIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM( + "application/vnd.sun.star.legacy-package-bundle") )) + { + // inflate content: + ::rtl::OUStringBuffer buf; + if (!sourceContent.isFolder()) + { + buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.zip://") ); + buf.append( ::rtl::Uri::encode( sourceContent.getURL(), + rtl_UriCharClassRegName, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ) ); + } + else + { + //Folder. No need to unzip, just copy + buf.append(sourceContent.getURL()); + } + buf.append( static_cast<sal_Unicode>('/') ); + sourceContent = ::ucbhelper::Content( + buf.makeStringAndClear(), xCmdEnv ); + } + if (! destFolderContent.transferContent( + sourceContent, ::ucbhelper::InsertOperation_COPY, + title, NameClash::OVERWRITE )) + throw RuntimeException( OUSTR("UCB transferContent() failed!"), 0 ); + + + // write to DB: + //bundled extensions should only be added by the synchronizeAddedExtensions + //functions. Moreover, there is no "temporary folder" for bundled extensions. + OSL_ASSERT(!m_context.equals(OUSTR("bundled"))); + OUString sFolderUrl = makeURLAppendSysPathSegment(destFolderContent.getURL(), title); + DescriptionInfoset info = + dp_misc::getDescriptionInfoset(sFolderUrl); + dbData->temporaryName = tempEntry; + dbData->fileName = title; + dbData->mediaType = mediaType; + dbData->version = info.getVersion(); + + //No write the properties file next to the extension + ExtensionProperties props(sFolderUrl, properties, xCmdEnv); + props.write(); + return destFolder; +} + +//______________________________________________________________________________ +void PackageManagerImpl::insertToActivationLayerDB( + OUString const & id, ActivePackages::Data const & dbData ) +{ + //access to the database must be guarded. See removePackage + const ::osl::MutexGuard guard( getMutex() ); + m_activePackagesDB->put( id, dbData ); +} + +//______________________________________________________________________________ +/* The function returns true if there is an extension with the same id already + installed which needs to be uninstalled, before the new extension can be installed. +*/ +bool PackageManagerImpl::isInstalled( + Reference<deployment::XPackage> const & package) +{ + OUString id(dp_misc::getIdentifier(package)); + OUString fn(package->getName()); + bool bInstalled = false; + if (m_activePackagesDB->has( id, fn )) + { + bInstalled = true; + } + return bInstalled; +} + +// XPackageManager +//______________________________________________________________________________ +Reference<deployment::XPackage> PackageManagerImpl::importExtension( + Reference<deployment::XPackage> const & extension, + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<XCommandEnvironment> const & xCmdEnv_ ) + throw (deployment::DeploymentException, CommandFailedException, + CommandAbortedException, lang::IllegalArgumentException, + RuntimeException) +{ + return addPackage(extension->getURL(), Sequence<beans::NamedValue>(), + OUString(), xAbortChannel, xCmdEnv_); +} + +/* The function adds an extension but does not register it!!! + It may not do any user interaction. This is done in XExtensionManager::addExtension +*/ +Reference<deployment::XPackage> PackageManagerImpl::addPackage( + OUString const & url, + css::uno::Sequence<css::beans::NamedValue> const & properties, + OUString const & mediaType_, + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<XCommandEnvironment> const & xCmdEnv_ ) + throw (deployment::DeploymentException, CommandFailedException, + CommandAbortedException, lang::IllegalArgumentException, + RuntimeException) +{ + check(); + if (m_readOnly) + { + OUString message; + if (m_context == OUSTR("shared")) + message = OUSTR("You need write permissions to install a shared extension!"); + else + message = OUSTR("You need write permissions to install this extension!"); + throw deployment::DeploymentException( + message, static_cast<OWeakObject *>(this), Any() ); + } + Reference<XCommandEnvironment> xCmdEnv; + if (m_xLogFile.is()) + xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) ); + else + xCmdEnv.set( xCmdEnv_ ); + + try { + ::ucbhelper::Content sourceContent; + create_ucb_content( &sourceContent, url, xCmdEnv ); // throws exc + const OUString title(sourceContent.getPropertyValue( + StrTitle::get() ).get<OUString>() ); + const OUString title_enc( ::rtl::Uri::encode( + title, rtl_UriCharClassPchar, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ) ); + OUString destFolder; + + OUString mediaType(mediaType_); + if (mediaType.getLength() == 0) + mediaType = detectMediaType( sourceContent ); + + Reference<deployment::XPackage> xPackage; + // copy file: + progressUpdate( + getResourceString(RID_STR_COPYING_PACKAGE) + title, xCmdEnv ); + if (m_activePackages.getLength() == 0) + { + ::ucbhelper::Content docFolderContent; + create_folder( &docFolderContent, m_context, xCmdEnv ); + // copy into document, first: + if (! docFolderContent.transferContent( + sourceContent, ::ucbhelper::InsertOperation_COPY, + OUString(), + NameClash::ASK /* xxx todo: ASK not needed? */)) + throw RuntimeException( + OUSTR("UCB transferContent() failed!"), 0 ); + // set media-type: + ::ucbhelper::Content docContent( + makeURL( m_context, title_enc ), xCmdEnv ); + //TODO #i73136#: using title instead of id can lead to + // clashes, but the whole m_activePackages.getLength()==0 + // case (i.e., document-relative deployment) currently does + // not work, anyway. + docContent.setPropertyValue( + OUSTR("MediaType"), Any(mediaType) ); + + // xxx todo: obsolete in the future + try { + docFolderContent.executeCommand( OUSTR("flush"), Any() ); + } + catch (UnsupportedCommandException &) { + } + } + ActivePackages::Data dbData; + destFolder = insertToActivationLayer( + properties, mediaType, sourceContent, title, &dbData ); + + + // bind activation package: + //Because every shared/user extension will be unpacked in a folder, + //which was created with a unique name we will always have two different + //XPackage objects, even if the second extension is the same. + //Therefore bindPackage does not need a guard here. + xPackage = m_xRegistry->bindPackage( + makeURL( destFolder, title_enc ), mediaType, false, OUString(), xCmdEnv ); + + OSL_ASSERT( xPackage.is() ); + if (xPackage.is()) + { + bool install = false; + try + { + OUString const id = dp_misc::getIdentifier( xPackage ); + + ::osl::MutexGuard g(m_addMutex); + if (isInstalled(xPackage)) + { + //Do not guard the complete function with the getMutex + removePackage(id, xPackage->getName(), xAbortChannel, + xCmdEnv); + } + install = true; + insertToActivationLayerDB(id, dbData); + } + catch (...) + { + deletePackageFromCache( xPackage, destFolder ); + throw; + } + if (!install) + { + deletePackageFromCache( xPackage, destFolder ); + } + //ToDo: We should notify only if the extension is registered + fireModified(); + } + return xPackage; + } + catch (RuntimeException &) { + throw; + } + catch (CommandFailedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (CommandAbortedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (deployment::DeploymentException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + logIntern( exc ); + throw deployment::DeploymentException( + getResourceString(RID_STR_ERROR_WHILE_ADDING) + url, + static_cast<OWeakObject *>(this), exc ); + } +} +void PackageManagerImpl::deletePackageFromCache( + Reference<deployment::XPackage> const & xPackage, + OUString const & destFolder) +{ + try_dispose( xPackage ); + + //we remove the package from the uno cache + //no service from the package may be loaded at this time!!! + erase_path( destFolder, Reference<XCommandEnvironment>(), + false /* no throw: ignore errors */ ); + //rm last character '_' + OUString url = destFolder.copy(0, destFolder.getLength() - 1); + erase_path( url, Reference<XCommandEnvironment>(), + false /* no throw: ignore errors */ ); + +} +//______________________________________________________________________________ +void PackageManagerImpl::removePackage( + OUString const & id, ::rtl::OUString const & fileName, + Reference<task::XAbortChannel> const & /*xAbortChannel*/, + Reference<XCommandEnvironment> const & xCmdEnv_ ) + throw (deployment::DeploymentException, CommandFailedException, + CommandAbortedException, lang::IllegalArgumentException, + RuntimeException) +{ + check(); + + Reference<XCommandEnvironment> xCmdEnv; + if (m_xLogFile.is()) + xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) ); + else + xCmdEnv.set( xCmdEnv_ ); + + try { + Reference<deployment::XPackage> xPackage; + { + const ::osl::MutexGuard guard(getMutex()); + //Check if this extension exist and throw an IllegalArgumentException + //if it does not + //If the files of the extension are already removed, or there is a + //different extension at the same place, for example after updating the + //extension, then the returned object is that which uses the database data. + xPackage = getDeployedPackage_(id, fileName, xCmdEnv ); + + + //Because the extension is only removed the next time the extension + //manager runs after restarting OOo, we need to indicate that a + //shared extension was "deleted". When a user starts OOo, then it + //will check if something changed in the shared repository. Based on + //the flag file it will then recognize, that the extension was + //deleted and can then update the extnesion database of the shared + //extensions in the user installation. + if ( xPackage.is() && !m_readOnly && !xPackage->isRemoved() && m_context.equals(OUSTR("shared"))) + { + ActivePackages::Data val; + m_activePackagesDB->get( & val, id, fileName); + OSL_ASSERT(val.temporaryName.getLength()); + OUString url(makeURL(m_activePackages_expanded, + val.temporaryName + OUSTR("removed"))); + ::ucbhelper::Content contentRemoved(url, xCmdEnv ); + OUString aUserName; + ::osl::Security aSecurity; + aSecurity.getUserName( aUserName ); + + ::rtl::OString stamp = ::rtl::OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8); + Reference<css::io::XInputStream> xData( + ::xmlscript::createInputStream( + ::rtl::ByteSequence( + reinterpret_cast<sal_Int8 const *>(stamp.getStr()), + stamp.getLength() ) ) ); + contentRemoved.writeStream( xData, true /* replace existing */ ); + } + m_activePackagesDB->erase( id, fileName ); // to be removed upon next start + //remove any cached data hold by the backend + m_xRegistry->packageRemoved(xPackage->getURL(), xPackage->getPackageType()->getMediaType()); + } + try_dispose( xPackage ); + + fireModified(); + } + catch (RuntimeException &) { + throw; + } + catch (lang::IllegalArgumentException &) { + throw; + } + catch (CommandFailedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (CommandAbortedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (deployment::DeploymentException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + logIntern( exc ); + throw deployment::DeploymentException( + getResourceString(RID_STR_ERROR_WHILE_REMOVING) + id, + static_cast<OWeakObject *>(this), exc ); + } +} + +//______________________________________________________________________________ +OUString PackageManagerImpl::getDeployPath( ActivePackages::Data const & data ) +{ + ::rtl::OUStringBuffer buf; + buf.append( data.temporaryName ); + //The bundled extensions are not contained in an additional folder + //with a unique name. data.temporaryName contains already the + //UTF8 encoded folder name. See PackageManagerImpl::synchronize + if (!m_context.equals(OUSTR("bundled")) + && !m_context.equals(OUSTR("bundled_prereg"))) + { + buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("_/") ); + buf.append( ::rtl::Uri::encode( data.fileName, rtl_UriCharClassPchar, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ) ); + } + return makeURL( m_activePackages, buf.makeStringAndClear() ); +} + +//______________________________________________________________________________ +Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_( + OUString const & id, OUString const & fileName, + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + ActivePackages::Data val; + if (m_activePackagesDB->get( &val, id, fileName )) + { + return getDeployedPackage_( id, val, xCmdEnv, false ); + } + throw lang::IllegalArgumentException( + getResourceString(RID_STR_NO_SUCH_PACKAGE) + id, + static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) ); +} + +//______________________________________________________________________________ +Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_( + OUString const & id, ActivePackages::Data const & data, + Reference<XCommandEnvironment> const & xCmdEnv, bool ignoreAlienPlatforms ) +{ + if (ignoreAlienPlatforms) + { + String type, subType; + INetContentTypeParameterList params; + if (INetContentTypes::parse( data.mediaType, type, subType, ¶ms )) + { + INetContentTypeParameter const * param = params.find( + ByteString("platform") ); + if (param != 0 && !platform_fits( param->m_sValue )) + throw lang::IllegalArgumentException( + getResourceString(RID_STR_NO_SUCH_PACKAGE) + id, + static_cast<OWeakObject *>(this), + static_cast<sal_Int16>(-1) ); + } + } + Reference<deployment::XPackage> xExtension; + try + { + //Ignore extensions where XPackage::checkPrerequisites failed. + //They must not be usable for this user. + if (data.failedPrerequisites.equals(OUSTR("0"))) + { + xExtension = m_xRegistry->bindPackage( + getDeployPath( data ), data.mediaType, false, OUString(), xCmdEnv ); + } + } + catch (deployment::InvalidRemovedParameterException& e) + { + xExtension = e.Extension; + } + return xExtension; +} + +//______________________________________________________________________________ +Sequence< Reference<deployment::XPackage> > +PackageManagerImpl::getDeployedPackages_( + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + ::std::vector< Reference<deployment::XPackage> > packages; + ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() ); + ActivePackages::Entries::const_iterator iPos( id2temp.begin() ); + ActivePackages::Entries::const_iterator const iEnd( id2temp.end() ); + for ( ; iPos != iEnd; ++iPos ) + { + if (! iPos->second.failedPrerequisites.equals(OUSTR("0"))) + continue; + try { + packages.push_back( + getDeployedPackage_( + iPos->first, iPos->second, xCmdEnv, + true /* xxx todo: think of GUI: + ignore other platforms than the current one */ ) ); + } + catch (lang::IllegalArgumentException & exc) { + // ignore + (void) exc; // avoid warnings + OSL_FAIL( ::rtl::OUStringToOString( + exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + catch (deployment::DeploymentException& exc) { + // ignore + (void) exc; // avoid warnings + OSL_FAIL( ::rtl::OUStringToOString( + exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + return comphelper::containerToSequence(packages); +} + +//______________________________________________________________________________ +Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage( + OUString const & id, ::rtl::OUString const & fileName, + Reference<XCommandEnvironment> const & xCmdEnv_ ) + throw (deployment::DeploymentException, CommandFailedException, + lang::IllegalArgumentException, RuntimeException) +{ + check(); + Reference<XCommandEnvironment> xCmdEnv; + if (m_xLogFile.is()) + xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) ); + else + xCmdEnv.set( xCmdEnv_ ); + + try { + const ::osl::MutexGuard guard( getMutex() ); + return getDeployedPackage_( id, fileName, xCmdEnv ); + } + catch (RuntimeException &) { + throw; + } + catch (CommandFailedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (lang::IllegalArgumentException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (deployment::DeploymentException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + logIntern( exc ); + throw deployment::DeploymentException( + // ought never occur... + OUSTR("error while accessing deployed package: ") + id, + static_cast<OWeakObject *>(this), exc ); + } +} + +//______________________________________________________________________________ +Sequence< Reference<deployment::XPackage> > +PackageManagerImpl::getDeployedPackages( + Reference<task::XAbortChannel> const &, + Reference<XCommandEnvironment> const & xCmdEnv_ ) + throw (deployment::DeploymentException, CommandFailedException, + CommandAbortedException, lang::IllegalArgumentException, + RuntimeException) +{ + check(); + Reference<XCommandEnvironment> xCmdEnv; + if (m_xLogFile.is()) + xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) ); + else + xCmdEnv.set( xCmdEnv_ ); + + try { + const ::osl::MutexGuard guard( getMutex() ); + return getDeployedPackages_( xCmdEnv ); + } + catch (RuntimeException &) { + throw; + } + catch (CommandFailedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (CommandAbortedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (deployment::DeploymentException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + logIntern( exc ); + throw deployment::DeploymentException( + // ought never occur... + OUSTR("error while getting all deployed packages: ") + m_context, + static_cast<OWeakObject *>(this), exc ); + } +} + +//______________________________________________________________________________ + + +//ToDo: the function must not call registerPackage, do this in +//XExtensionManager.reinstallDeployedExtensions +void PackageManagerImpl::reinstallDeployedPackages( + Reference<task::XAbortChannel> const & /*xAbortChannel*/, + Reference<XCommandEnvironment> const & xCmdEnv_ ) + throw (deployment::DeploymentException, + CommandFailedException, CommandAbortedException, + lang::IllegalArgumentException, RuntimeException) +{ + check(); + if (office_is_running()) + throw RuntimeException( + OUSTR("You must close any running Office process before " + "reinstalling packages!"), static_cast<OWeakObject *>(this) ); + + Reference<XCommandEnvironment> xCmdEnv; + if (m_xLogFile.is()) + xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) ); + else + xCmdEnv.set( xCmdEnv_ ); + + try { + ProgressLevel progress( + xCmdEnv, OUSTR("Reinstalling all deployed packages...") ); + + try_dispose( m_xRegistry ); + m_xRegistry.clear(); + if (m_registryCache.getLength() > 0) + erase_path( m_registryCache, xCmdEnv ); + initRegistryBackends(); + Reference<util::XUpdatable> xUpdatable( m_xRegistry, UNO_QUERY ); + if (xUpdatable.is()) + xUpdatable->update(); + + //registering is done by the ExtensionManager service. + } + catch (RuntimeException &) { + throw; + } + catch (CommandFailedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (CommandAbortedException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (deployment::DeploymentException & exc) { + logIntern( Any(exc) ); + throw; + } + catch (Exception &) { + Any exc( ::cppu::getCaughtException() ); + logIntern( exc ); + throw deployment::DeploymentException( + OUSTR("Error while reinstalling all previously deployed " + "packages of context ") + m_context, + static_cast<OWeakObject *>(this), exc ); + } +} + + +::sal_Bool SAL_CALL PackageManagerImpl::isReadOnly( ) + throw (::com::sun::star::uno::RuntimeException) +{ + return m_readOnly; +} +bool PackageManagerImpl::synchronizeRemovedExtensions( + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) +{ + + //find all which are in the extension data base but which + //are removed already. + OSL_ASSERT(!m_context.equals(OUSTR("user"))); + bool bModified = false; + ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() ); + + typedef ActivePackages::Entries::const_iterator ITActive; + bool bShared = m_context.equals(OUSTR("shared")); + + for (ITActive i = id2temp.begin(); i != id2temp.end(); ++i) + { + try + { + //Get the URL to the extensions folder, first make the url for the + //shared repository including the temporary name + OUString url = makeURL(m_activePackages, i->second.temporaryName); + if (bShared) + url = makeURLAppendSysPathSegment( url + OUSTR("_"), i->second.fileName); + + bool bRemoved = false; + //Check if the URL to the extension is still the same + ::ucbhelper::Content contentExtension; + + if (!create_ucb_content( + &contentExtension, url, + Reference<XCommandEnvironment>(), false)) + { + bRemoved = true; + } + + //The folder is in the extension database, but it can still be deleted. + //look for the xxx.tmpremoved file + //There can also be the case that a different extension was installed + //in a "temp" folder with name that is already used. + if (!bRemoved && bShared) + { + ::ucbhelper::Content contentRemoved; + + if (create_ucb_content( + &contentRemoved, + m_activePackages_expanded + OUSTR("/") + + i->second.temporaryName + OUSTR("removed"), + Reference<XCommandEnvironment>(), false)) + { + bRemoved = true; + } + } + + if (!bRemoved) + { + //There may be another extensions at the same place + dp_misc::DescriptionInfoset infoset = + dp_misc::getDescriptionInfoset(url); + OSL_ENSURE(infoset.hasDescription() && infoset.getIdentifier(), + "Extension Manager: bundled and shared extensions " + "must have an identifer and a version"); + if (infoset.hasDescription() && + infoset.getIdentifier() && + (! i->first.equals(*(infoset.getIdentifier())) + || ! i->second.version.equals(infoset.getVersion()))) + { + bRemoved = true; + } + + } + if (bRemoved) + { + Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage( + url, i->second.mediaType, true, i->first, xCmdEnv ); + OSL_ASSERT(xPackage.is()); //Even if the files are removed, we must get the object. + xPackage->revokePackage(xAbortChannel, xCmdEnv); + removePackage(xPackage->getIdentifier().Value, xPackage->getName(), + xAbortChannel, xCmdEnv); + bModified |= true; + } + } + catch( uno::Exception & ) + { + OSL_ASSERT(0); + } + } + return bModified; +} + + +bool PackageManagerImpl::synchronizeAddedExtensions( + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) +{ + bool bModified = false; + OSL_ASSERT(!m_context.equals(OUSTR("user"))); + + ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() ); + //check if the folder exist at all. The shared extension folder + //may not exist for a normal user. + if (!create_ucb_content( + NULL, m_activePackages_expanded, Reference<css::ucb::XCommandEnvironment>(), false)) + return bModified; + ::ucbhelper::Content tempFolder( + m_activePackages_expanded, xCmdEnv ); + + Reference<sdbc::XResultSet> xResultSet( + tempFolder.createCursor( + Sequence<OUString>( &StrTitle::get(), 1 ), + ::ucbhelper::INCLUDE_FOLDERS_ONLY ) ); + + while (xResultSet->next()) + { + try + { + OUString title( + Reference<sdbc::XRow>( + xResultSet, UNO_QUERY_THROW )->getString( + 1 /* Title */ ) ); + //The temporary folders of user and shared have an '_' at then end. + //But the name in ActivePackages.temporaryName is saved without. + OUString title2 = title; + bool bShared = m_context.equals(OUSTR("shared")); + if (bShared) + { + OSL_ASSERT(title2[title2.getLength() -1] == '_'); + title2 = title2.copy(0, title2.getLength() -1); + } + OUString titleEncoded = ::rtl::Uri::encode( + title2, rtl_UriCharClassPchar, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8); + + //It it sufficient to check for the folder name, because when the administor + //installed the extension it was already checked if there is one with the + //same identifier. + const MatchTempDir match(titleEncoded); + if (::std::find_if( id2temp.begin(), id2temp.end(), match ) == + id2temp.end()) + { + + // The folder was not found in the data base, so it must be + // an added extension + OUString url(m_activePackages_expanded + OUSTR("/") + titleEncoded); + OUString sExtFolder; + if (bShared) //that is, shared + { + //Check if the extension was not "deleted" already which is indicated + //by a xxx.tmpremoved file + ::ucbhelper::Content contentRemoved; + if (create_ucb_content(&contentRemoved, url + OUSTR("removed"), + Reference<XCommandEnvironment>(), false)) + continue; + sExtFolder = getExtensionFolder( + m_activePackages_expanded + + OUString(OUSTR("/")) + titleEncoded + OUSTR("_"), xCmdEnv); + url = makeURLAppendSysPathSegment(m_activePackages_expanded, title); + url = makeURLAppendSysPathSegment(url, sExtFolder); + } + Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage( + url, OUString(), false, OUString(), xCmdEnv ); + if (xPackage.is()) + { + //Prepare the database entry + ActivePackages::Data dbData; + + dbData.temporaryName = titleEncoded; + if (bShared) + dbData.fileName = sExtFolder; + else + dbData.fileName = title; + dbData.mediaType = xPackage->getPackageType()->getMediaType(); + dbData.version = xPackage->getVersion(); + OSL_ENSURE(dbData.version.getLength() > 0, + "Extension Manager: bundled and shared extensions must have " + "an identifier and a version"); + + OUString id = dp_misc::getIdentifier( xPackage ); + + //We provide a special command environment that will prevent + //showing a license if simple-licens/@accept-by = "admin" + //It will also prevent showing the license for bundled extensions + //which is not supported. + OSL_ASSERT(!m_context.equals(OUSTR("user"))); + + // shall the license be suppressed? + DescriptionInfoset info = + dp_misc::getDescriptionInfoset(url); + ::boost::optional<dp_misc::SimpleLicenseAttributes> + attr = info.getSimpleLicenseAttributes(); + ExtensionProperties props(url,xCmdEnv); + bool bNoLicense = false; + if (attr && attr->suppressIfRequired && props.isSuppressedLicense()) + bNoLicense = true; + + Reference<ucb::XCommandEnvironment> licCmdEnv( + new LicenseCommandEnv(xCmdEnv->getInteractionHandler(), + bNoLicense, m_context)); + sal_Int32 failedPrereq = xPackage->checkPrerequisites( + xAbortChannel, licCmdEnv, false); + //Remember that this failed. For example, the user + //could have declined the license. Then the next time the + //extension folder is investigated we do not want to + //try to install the extension again. + dbData.failedPrerequisites = OUString::valueOf(failedPrereq); + insertToActivationLayerDB(id, dbData); + bModified |= true; + } + } + } + catch (uno::Exception &) + { + OSL_ASSERT(0); + } + } + return bModified; +} + +sal_Bool PackageManagerImpl::synchronize( + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) + throw (css::deployment::DeploymentException, + css::ucb::CommandFailedException, + css::ucb::CommandAbortedException, + css::uno::RuntimeException) +{ + check(); + bool bModified = false; + if (m_context.equals(OUSTR("user"))) + return bModified; + bModified |= + synchronizeRemovedExtensions(xAbortChannel, xCmdEnv); + bModified |= synchronizeAddedExtensions(xAbortChannel, xCmdEnv); + + return bModified; +} + +Sequence< Reference<deployment::XPackage> > PackageManagerImpl::getExtensionsWithUnacceptedLicenses( + Reference<ucb::XCommandEnvironment> const & xCmdEnv) + throw (deployment::DeploymentException, RuntimeException) +{ + ::std::vector<Reference<deployment::XPackage> > vec; + + try + { + const ::osl::MutexGuard guard( getMutex() ); + // clean up activation layer, scan for zombie temp dirs: + ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() ); + + ActivePackages::Entries::const_iterator i = id2temp.begin(); + bool bShared = m_context.equals(OUSTR("shared")); + + for (; i != id2temp.end(); ++i ) + { + //Get the database entry + ActivePackages::Data const & dbData = i->second; + sal_Int32 failedPrereq = dbData.failedPrerequisites.toInt32(); + //If the installation failed for other reason then the license then we + //ignore it. + if (failedPrereq ^= deployment::Prerequisites::LICENSE) + continue; + + //Prepare the URL to the extension + OUString url = makeURL(m_activePackages, i->second.temporaryName); + if (bShared) + url = makeURLAppendSysPathSegment( url + OUSTR("_"), i->second.fileName); + + Reference<deployment::XPackage> p = m_xRegistry->bindPackage( + url, OUString(), false, OUString(), xCmdEnv ); + + if (p.is()) + vec.push_back(p); + + } + return ::comphelper::containerToSequence(vec); + } + catch (deployment::DeploymentException &) + { + throw; + } + catch (RuntimeException&) + { + throw; + } + catch (...) + { + Any exc = ::cppu::getCaughtException(); + deployment::DeploymentException de( + OUSTR("PackageManagerImpl::getExtensionsWithUnacceptedLicenses"), + static_cast<OWeakObject*>(this), exc); + exc <<= de; + ::cppu::throwException(exc); + } + + return ::comphelper::containerToSequence(vec); +} + +sal_Int32 PackageManagerImpl::checkPrerequisites( + css::uno::Reference<css::deployment::XPackage> const & extension, + css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel, + css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) + throw (css::deployment::DeploymentException, + css::ucb::CommandFailedException, + css::ucb::CommandAbortedException, + css::lang::IllegalArgumentException, + css::uno::RuntimeException) +{ + try + { + if (!extension.is()) + return 0; + if (!m_context.equals(extension->getRepositoryName())) + throw lang::IllegalArgumentException( + OUSTR("PackageManagerImpl::checkPrerequisites: extension is not" + " from this repository."), 0, 0); + + ActivePackages::Data dbData; + OUString id = dp_misc::getIdentifier(extension); + if (m_activePackagesDB->get( &dbData, id, OUString())) + { + //If the license was already displayed, then do not show it again + Reference<ucb::XCommandEnvironment> _xCmdEnv = xCmdEnv; + sal_Int32 prereq = dbData.failedPrerequisites.toInt32(); + if ( !(prereq & deployment::Prerequisites::LICENSE)) + _xCmdEnv = new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler()); + + sal_Int32 failedPrereq = extension->checkPrerequisites( + xAbortChannel, _xCmdEnv, false); + dbData.failedPrerequisites = OUString::valueOf(failedPrereq); + insertToActivationLayerDB(id, dbData); + } + else + { + throw lang::IllegalArgumentException( + OUSTR("PackageManagerImpl::checkPrerequisites: unknown extension"), + 0, 0); + + } + return 0; + } + catch (deployment::DeploymentException& ) { + throw; + } catch (ucb::CommandFailedException & ) { + throw; + } catch (ucb::CommandAbortedException & ) { + throw; + } catch (lang::IllegalArgumentException &) { + throw; + } catch (uno::RuntimeException &) { + throw; + } catch (...) { + uno::Any excOccurred = ::cppu::getCaughtException(); + deployment::DeploymentException exc( + OUSTR("PackageManagerImpl::checkPrerequisites: exception "), + static_cast<OWeakObject*>(this), excOccurred); + throw exc; + } +} + + +//______________________________________________________________________________ +PackageManagerImpl::CmdEnvWrapperImpl::~CmdEnvWrapperImpl() +{ +} + +//______________________________________________________________________________ +PackageManagerImpl::CmdEnvWrapperImpl::CmdEnvWrapperImpl( + Reference<XCommandEnvironment> const & xUserCmdEnv, + Reference<XProgressHandler> const & xLogFile ) + : m_xLogFile( xLogFile ) +{ + if (xUserCmdEnv.is()) { + m_xUserProgress.set( xUserCmdEnv->getProgressHandler() ); + m_xUserInteractionHandler.set( xUserCmdEnv->getInteractionHandler() ); + } +} + +// XCommandEnvironment +//______________________________________________________________________________ +Reference<task::XInteractionHandler> +PackageManagerImpl::CmdEnvWrapperImpl::getInteractionHandler() + throw (RuntimeException) +{ + return m_xUserInteractionHandler; +} + +//______________________________________________________________________________ +Reference<XProgressHandler> +PackageManagerImpl::CmdEnvWrapperImpl::getProgressHandler() + throw (RuntimeException) +{ + return this; +} + +// XProgressHandler +//______________________________________________________________________________ +void PackageManagerImpl::CmdEnvWrapperImpl::push( Any const & Status ) + throw (RuntimeException) +{ + if (m_xLogFile.is()) + m_xLogFile->push( Status ); + if (m_xUserProgress.is()) + m_xUserProgress->push( Status ); +} + +//______________________________________________________________________________ +void PackageManagerImpl::CmdEnvWrapperImpl::update( Any const & Status ) + throw (RuntimeException) +{ + if (m_xLogFile.is()) + m_xLogFile->update( Status ); + if (m_xUserProgress.is()) + m_xUserProgress->update( Status ); +} + +//______________________________________________________________________________ +void PackageManagerImpl::CmdEnvWrapperImpl::pop() throw (RuntimeException) +{ + if (m_xLogFile.is()) + m_xLogFile->pop(); + if (m_xUserProgress.is()) + m_xUserProgress->pop(); +} + +} // namespace dp_manager + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |