| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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(".pmap"); |
| else |
| { |
| //Create the extension data base in the user installation |
| create_folder( 0, m_registrationData_expanded, xCmdEnv, true); |
| dbName = m_registrationData_expanded + OUSTR("/extensions.pmap"); |
| } |
| //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_PREREG"); |
| 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.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("bak") )) { |
| that->m_activePackages = OUSTR( |
| "vnd.sun.star.expand:$BAK_EXTENSIONS/extensions"); |
| that->m_registrationData = OUSTR( |
| "vnd.sun.star.expand:$BAK_EXTENSIONS"); |
| that->m_registryCache = OUSTR( |
| "vnd.sun.star.expand:$BAK_EXTENSIONS/registry"); |
| stampURL = OUSTR( |
| "vnd.sun.star.expand:$BAK_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) |
| { |
| //Do not call check here. We must not throw an exception here if the object |
| //is being disposed or is already disposed. See com.sun.star.lang.XComponent |
| WeakComponentImplHelperBase::dispose(); |
| } |
| |
| //______________________________________________________________________________ |
| void PackageManagerImpl::addEventListener( |
| Reference<lang::XEventListener> const & xListener ) throw (RuntimeException) |
| { |
| //Do not call check here. We must not throw an exception here if the object |
| //is being disposed or is already disposed. See com.sun.star.lang.XComponent |
| WeakComponentImplHelperBase::addEventListener( xListener ); |
| } |
| |
| //______________________________________________________________________________ |
| void PackageManagerImpl::removeEventListener( |
| Reference<lang::XEventListener> const & xListener ) throw (RuntimeException) |
| { |
| //Do not call check here. We must not throw an exception here if the object |
| //is being disposed or is already disposed. See com.sun.star.lang.XComponent |
| 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_ENSURE( 0, ::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_ENSURE( 0, ::rtl::OUStringToOString( |
| exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| catch (deployment::DeploymentException& exc) { |
| // ignore |
| (void) exc; // avoid warnings |
| OSL_ENSURE( 0, ::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(), |
| "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 &) |
| { |
| // Looks like exceptions being caught here is not an |
| // uncommon case. |
| OSL_TRACE("caught exception in PackageManagerImpl::synchronizeAddedExtensions"); |
| } |
| } |
| 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 |
| |