| /************************************************************** |
| * |
| * 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_package.hrc" |
| #include "dp_backend.h" |
| #include "dp_ucb.h" |
| #include "dp_interact.h" |
| #include "dp_dependencies.hxx" |
| #include "dp_platform.hxx" |
| #include "dp_descriptioninfoset.hxx" |
| #include "dp_identifier.hxx" |
| #include "rtl/uri.hxx" |
| #include "cppuhelper/exc_hlp.hxx" |
| #include "cppuhelper/implbase1.hxx" |
| #include "ucbhelper/content.hxx" |
| #include "svl/inettype.hxx" |
| #include "comphelper/anytostring.hxx" |
| #include "comphelper/makesequence.hxx" |
| #include "comphelper/sequence.hxx" |
| #include "com/sun/star/lang/WrappedTargetException.hpp" |
| #include "com/sun/star/lang/XServiceInfo.hpp" |
| #include "com/sun/star/beans/UnknownPropertyException.hpp" |
| #include "com/sun/star/graphic/XGraphic.hpp" |
| #include "com/sun/star/graphic/XGraphicProvider.hpp" |
| #include "com/sun/star/io/XOutputStream.hpp" |
| #include "com/sun/star/io/XInputStream.hpp" |
| #include "com/sun/star/task/InteractionClassification.hpp" |
| #include "com/sun/star/task/XInteractionApprove.hpp" |
| #include "com/sun/star/ucb/XInteractionReplaceExistingData.hpp" |
| #include "com/sun/star/ucb/NameClashResolveRequest.hpp" |
| #include "com/sun/star/ucb/XContentAccess.hpp" |
| #include "com/sun/star/ucb/NameClash.hpp" |
| #include "com/sun/star/ucb/UnsupportedCommandException.hpp" |
| #include "com/sun/star/sdbc/XResultSet.hpp" |
| #include "com/sun/star/sdbc/XRow.hpp" |
| #include "com/sun/star/packages/manifest/XManifestReader.hpp" |
| #include "com/sun/star/packages/manifest/XManifestWriter.hpp" |
| #include "com/sun/star/deployment/DependencyException.hpp" |
| #include "com/sun/star/deployment/LicenseException.hpp" |
| #include "com/sun/star/deployment/PlatformException.hpp" |
| #include "com/sun/star/deployment/Prerequisites.hpp" |
| #include "com/sun/star/xml/dom/XDocumentBuilder.hpp" |
| #include "com/sun/star/xml/xpath/XXPathAPI.hpp" |
| #include "com/sun/star/deployment/XPackageManager.hpp" |
| #include "boost/optional.hpp" |
| #include <vector> |
| #include <stdio.h> |
| |
| #include "dp_extbackenddb.hxx" |
| using namespace ::dp_misc; |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::uno; |
| |
| namespace css = ::com::sun::star; |
| |
| using ::rtl::OUString; |
| |
| namespace dp_registry { |
| namespace backend { |
| namespace bundle { |
| namespace { |
| |
| typedef cppu::ImplInheritanceHelper1<PackageRegistryBackend, |
| lang::XServiceInfo> ImplBaseT; |
| |
| //============================================================================== |
| class BackendImpl : public ImplBaseT |
| { |
| class PackageImpl : public ::dp_registry::backend::Package |
| { |
| BackendImpl * getMyBackend() const; |
| /** constains the old tooltip description for the Extension Manager GUI in OOo v.2.x |
| We keep it for backward compatibility. |
| */ |
| OUString m_oldDescription; |
| OUString m_url_expanded; |
| const bool m_legacyBundle; |
| Sequence< Reference<deployment::XPackage> > m_bundle; |
| Sequence< Reference<deployment::XPackage> > * m_pBundle; |
| |
| ExtensionBackendDb::Data m_dbData; |
| |
| Reference<deployment::XPackage> bindBundleItem( |
| OUString const & url, OUString const & mediaType, |
| sal_Bool bRemoved, //that is, using data base information |
| OUString const & identifier, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv, |
| bool notifyDetectionError = true ); |
| |
| typedef ::std::vector< Reference<deployment::XPackage> > t_packagevec; |
| void scanBundle( |
| t_packagevec & bundle, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ); |
| void scanLegacyBundle( |
| t_packagevec & bundle, |
| OUString const & url, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv, |
| bool skip_registration = false ); |
| ::std::vector<Reference<deployment::XPackage> > getPackagesFromDb( |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv); |
| bool checkPlatform( |
| Reference<ucb::XCommandEnvironment > const & environment); |
| |
| bool checkDependencies( |
| Reference<ucb::XCommandEnvironment > const & |
| environment, |
| DescriptionInfoset const & description); |
| // throws css::uno::RuntimeException, |
| // css::deployment::DeploymentException |
| |
| ::sal_Bool checkLicense( |
| Reference< ucb::XCommandEnvironment > const & xCmdEnv, |
| DescriptionInfoset const & description, bool bNoLicenseChecking) |
| throw (deployment::DeploymentException, |
| ucb::CommandFailedException, |
| ucb::CommandAbortedException, |
| RuntimeException); |
| // @throws DeploymentException |
| OUString getTextFromURL( |
| const Reference< ucb::XCommandEnvironment >& xCmdEnv, |
| const OUString& licenseUrl); |
| |
| DescriptionInfoset getDescriptionInfoset(); |
| |
| // Package |
| virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_( |
| ::osl::ResettableMutexGuard & guard, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ); |
| virtual void processPackage_( |
| ::osl::ResettableMutexGuard & guard, |
| bool registerPackage, |
| bool startup, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ); |
| |
| virtual void SAL_CALL disposing(); |
| |
| |
| |
| public: |
| PackageImpl( |
| ::rtl::Reference<PackageRegistryBackend> const & myBackend, |
| OUString const & url, |
| OUString const & name, |
| Reference<deployment::XPackageTypeInfo> const & xPackageType, |
| bool legacyBundle, |
| bool bRemoved, |
| OUString const & identifier); |
| |
| // XPackage |
| virtual sal_Bool SAL_CALL isBundle() throw (RuntimeException); |
| |
| virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle( |
| Reference<task::XAbortChannel> const & xAbortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| throw (deployment::DeploymentException, |
| ucb::CommandFailedException, |
| ucb::CommandAbortedException, |
| lang::IllegalArgumentException, RuntimeException); |
| virtual OUString SAL_CALL getDescription() |
| throw (deployment::ExtensionRemovedException, RuntimeException); |
| |
| virtual OUString SAL_CALL getLicenseText() |
| throw (deployment::ExtensionRemovedException, RuntimeException); |
| |
| virtual void SAL_CALL exportTo( |
| OUString const & destFolderURL, OUString const & newTitle, |
| sal_Int32 nameClashAction, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| throw (deployment::ExtensionRemovedException, |
| ucb::CommandFailedException, |
| ucb::CommandAbortedException, |
| RuntimeException); |
| |
| virtual ::sal_Int32 SAL_CALL checkPrerequisites( |
| const Reference< task::XAbortChannel >& xAbortChannel, |
| const Reference< ucb::XCommandEnvironment >& xCmdEnv, |
| ::sal_Bool noLicenseChecking) |
| throw (deployment::ExtensionRemovedException, |
| deployment::DeploymentException, |
| ucb::CommandFailedException, |
| ucb::CommandAbortedException, |
| RuntimeException); |
| |
| virtual ::sal_Bool SAL_CALL checkDependencies( |
| const Reference< ucb::XCommandEnvironment >& xCmdEnv ) |
| throw (deployment::DeploymentException, |
| deployment::ExtensionRemovedException, |
| ucb::CommandFailedException, |
| RuntimeException); |
| |
| virtual beans::Optional<OUString> SAL_CALL getIdentifier() |
| throw (RuntimeException); |
| |
| virtual OUString SAL_CALL getVersion() |
| throw (deployment::ExtensionRemovedException, RuntimeException); |
| |
| virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() |
| throw (deployment::ExtensionRemovedException, RuntimeException); |
| |
| virtual beans::StringPair SAL_CALL getPublisherInfo() |
| throw (deployment::ExtensionRemovedException, RuntimeException); |
| |
| virtual OUString SAL_CALL getDisplayName() |
| throw (deployment::ExtensionRemovedException, RuntimeException); |
| |
| virtual Reference< graphic::XGraphic > SAL_CALL |
| getIcon( ::sal_Bool bHighContrast ) |
| throw (deployment::ExtensionRemovedException, |
| RuntimeException); |
| }; |
| friend class PackageImpl; |
| |
| Reference<deployment::XPackageRegistry> m_xRootRegistry; |
| const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo; |
| const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo; |
| Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos; |
| |
| std::auto_ptr<ExtensionBackendDb> m_backendDb; |
| |
| void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data); |
| ExtensionBackendDb::Data readDataFromDb(OUString const & url); |
| void revokeEntryFromDb(OUString const & url); |
| |
| // PackageRegistryBackend |
| virtual Reference<deployment::XPackage> bindPackage_( |
| OUString const & url, OUString const & mediaType, |
| sal_Bool bRemoved, OUString const & identifier, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ); |
| |
| virtual void SAL_CALL disposing(); |
| |
| public: |
| BackendImpl( |
| Sequence<Any> const & args, |
| Reference<XComponentContext> const & xComponentContext, |
| Reference<deployment::XPackageRegistry> const & xRootRegistry ); |
| |
| // XServiceInfo |
| virtual OUString SAL_CALL getImplementationName() throw (RuntimeException); |
| virtual sal_Bool SAL_CALL supportsService( OUString const& name ) |
| throw (RuntimeException); |
| virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() |
| throw (RuntimeException); |
| |
| // XPackageRegistry |
| virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL |
| getSupportedPackageTypes() throw (RuntimeException); |
| virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) |
| throw (deployment::DeploymentException, |
| uno::RuntimeException); |
| |
| using ImplBaseT::disposing; |
| }; |
| |
| //Used to find a XPackage with a particular URL |
| class XPackage_eq : public std::unary_function<Reference<deployment::XPackage>, bool> |
| { |
| OUString m_URL; |
| public: |
| explicit XPackage_eq(const OUString & s) : m_URL(s) {} |
| bool operator() (const Reference<deployment::XPackage> & p) const |
| { |
| return m_URL.equals(p->getURL()); |
| } |
| }; |
| |
| //______________________________________________________________________________ |
| BackendImpl::BackendImpl( |
| Sequence<Any> const & args, |
| Reference<XComponentContext> const & xComponentContext, |
| Reference<deployment::XPackageRegistry> const & xRootRegistry ) |
| : ImplBaseT( args, xComponentContext ), |
| m_xRootRegistry( xRootRegistry ), |
| m_xBundleTypeInfo( new Package::TypeInfo( |
| OUSTR("application/vnd.sun.star.package-bundle"), |
| OUSTR("*.oxt;*.uno.pkg"), |
| getResourceString(RID_STR_PACKAGE_BUNDLE), |
| RID_IMG_DEF_PACKAGE_BUNDLE, |
| RID_IMG_DEF_PACKAGE_BUNDLE_HC ) ), |
| m_xLegacyBundleTypeInfo( new Package::TypeInfo( |
| OUSTR("application/" |
| "vnd.sun.star.legacy-package-bundle"), |
| OUSTR("*.zip"), |
| m_xBundleTypeInfo->getShortDescription(), |
| RID_IMG_DEF_PACKAGE_BUNDLE, |
| RID_IMG_DEF_PACKAGE_BUNDLE_HC ) ), |
| m_typeInfos(2) |
| { |
| m_typeInfos[ 0 ] = m_xBundleTypeInfo; |
| m_typeInfos[ 1 ] = m_xLegacyBundleTypeInfo; |
| |
| if (!transientMode()) |
| { |
| OUString dbFile = makeURL(getCachePath(), getImplementationName()); |
| dbFile = makeURL(dbFile, OUSTR("backenddb.xml")); |
| m_backendDb.reset( |
| new ExtensionBackendDb(getComponentContext(), dbFile)); |
| } |
| } |
| |
| //______________________________________________________________________________ |
| void BackendImpl::disposing() |
| { |
| m_xRootRegistry.clear(); |
| PackageRegistryBackend::disposing(); |
| } |
| |
| // XServiceInfo |
| OUString BackendImpl::getImplementationName() throw (RuntimeException) |
| { |
| return OUSTR("com.sun.star.comp.deployment.bundle.PackageRegistryBackend"); |
| } |
| |
| sal_Bool BackendImpl::supportsService( OUString const& name ) |
| throw (RuntimeException) |
| { |
| return getSupportedServiceNames()[0].equals(name); |
| } |
| |
| Sequence<OUString> BackendImpl::getSupportedServiceNames() |
| throw (RuntimeException) |
| { |
| return comphelper::makeSequence( |
| OUString::createFromAscii(BACKEND_SERVICE_NAME) ); |
| } |
| |
| // XPackageRegistry |
| //______________________________________________________________________________ |
| Sequence< Reference<deployment::XPackageTypeInfo> > |
| BackendImpl::getSupportedPackageTypes() throw (RuntimeException) |
| { |
| return m_typeInfos; |
| } |
| |
| void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/) |
| throw (deployment::DeploymentException, |
| uno::RuntimeException) |
| { |
| //Notify the backend responsible for processing the different media |
| //types that this extension was removed. |
| ExtensionBackendDb::Data data = readDataFromDb(url); |
| for (ExtensionBackendDb::Data::ITC_ITEMS i = data.items.begin(); i != data.items.end(); i++) |
| { |
| m_xRootRegistry->packageRemoved(i->first, i->second); |
| } |
| |
| if (m_backendDb.get()) |
| m_backendDb->removeEntry(url); |
| } |
| |
| |
| // PackageRegistryBackend |
| //______________________________________________________________________________ |
| Reference<deployment::XPackage> BackendImpl::bindPackage_( |
| OUString const & url, OUString const & mediaType_, |
| sal_Bool bRemoved, OUString const & identifier, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| { |
| OUString mediaType( mediaType_ ); |
| if (mediaType.getLength() == 0) |
| { |
| // detect media-type: |
| ::ucbhelper::Content ucbContent; |
| if (create_ucb_content( &ucbContent, url, xCmdEnv )) |
| { |
| if (ucbContent.isFolder()) |
| { |
| //Every .oxt, uno.pkg file must contain a META-INF folder |
| ::ucbhelper::Content metaInfContent; |
| if (create_ucb_content( |
| &metaInfContent, makeURL( url, OUSTR("META-INF") ), |
| xCmdEnv, false /* no throw */ )) |
| { |
| mediaType = OUSTR("application/vnd.sun.star.package-bundle"); |
| } |
| //No support of legacy bundles, because every folder could be one. |
| } |
| else |
| { |
| const OUString title( ucbContent.getPropertyValue( |
| StrTitle::get() ).get<OUString>() ); |
| if (title.endsWithIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM(".oxt") ) || |
| title.endsWithIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM(".uno.pkg") )) |
| mediaType = OUSTR("application/vnd.sun.star.package-bundle"); |
| else if (title.endsWithIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM(".zip") )) |
| mediaType = |
| OUSTR("application/vnd.sun.star.legacy-package-bundle"); |
| } |
| } |
| if (mediaType.getLength() == 0) |
| throw lang::IllegalArgumentException( |
| StrCannotDetectMediaType::get() + url, |
| static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) ); |
| } |
| |
| String type, subType; |
| INetContentTypeParameterList params; |
| if (INetContentTypes::parse( mediaType, type, subType, ¶ms )) |
| { |
| if (type.EqualsIgnoreCaseAscii("application")) |
| { |
| |
| //In case a XPackage is created for a removed extension, we cannot |
| //obtain the name |
| OUString name; |
| if (!bRemoved) |
| { |
| ::ucbhelper::Content ucbContent( url, xCmdEnv ); |
| name = ucbContent.getPropertyValue( |
| StrTitle::get() ).get<OUString>(); |
| } |
| if (subType.EqualsIgnoreCaseAscii("vnd.sun.star.package-bundle")) { |
| return new PackageImpl( |
| this, url, name, m_xBundleTypeInfo, false, bRemoved, |
| identifier); |
| } |
| else if (subType.EqualsIgnoreCaseAscii( |
| "vnd.sun.star.legacy-package-bundle")) { |
| return new PackageImpl( |
| this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved, |
| identifier); |
| } |
| } |
| } |
| throw lang::IllegalArgumentException( |
| StrUnsupportedMediaType::get() + mediaType, |
| static_cast<OWeakObject *>(this), |
| static_cast<sal_Int16>(-1) ); |
| } |
| |
| void BackendImpl::addDataToDb( |
| OUString const & url, ExtensionBackendDb::Data const & data) |
| { |
| if (m_backendDb.get()) |
| m_backendDb->addEntry(url, data); |
| } |
| |
| ExtensionBackendDb::Data BackendImpl::readDataFromDb( |
| OUString const & url) |
| { |
| ExtensionBackendDb::Data data; |
| if (m_backendDb.get()) |
| data = m_backendDb->getEntry(url); |
| return data; |
| } |
| |
| void BackendImpl::revokeEntryFromDb(OUString const & url) |
| { |
| if (m_backendDb.get()) |
| m_backendDb->revokeEntry(url); |
| } |
| |
| |
| //############################################################################## |
| |
| BackendImpl::PackageImpl::PackageImpl( |
| ::rtl::Reference<PackageRegistryBackend> const & myBackend, |
| OUString const & url, |
| OUString const & name, |
| Reference<deployment::XPackageTypeInfo> const & xPackageType, |
| bool legacyBundle, bool bRemoved, OUString const & identifier) |
| : Package( myBackend, url, name, name /* display-name */, |
| xPackageType, bRemoved, identifier), |
| m_url_expanded( expandUnoRcUrl( url ) ), |
| m_legacyBundle( legacyBundle ), |
| m_pBundle( 0 ) |
| { |
| if (bRemoved) |
| m_dbData = getMyBackend()->readDataFromDb(url); |
| } |
| |
| BackendImpl * BackendImpl::PackageImpl::getMyBackend() const |
| { |
| BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get()); |
| if (NULL == pBackend) |
| { |
| //May throw a DisposedException |
| check(); |
| //We should never get here... |
| throw RuntimeException( |
| OUSTR("Failed to get the BackendImpl"), |
| static_cast<OWeakObject*>(const_cast<PackageImpl *>(this))); |
| } |
| return pBackend; |
| } |
| //______________________________________________________________________________ |
| void BackendImpl::PackageImpl::disposing() |
| { |
| sal_Int32 len = m_bundle.getLength(); |
| Reference<deployment::XPackage> const * p = m_bundle.getConstArray(); |
| for ( sal_Int32 pos = 0; pos < len; ++pos ) |
| try_dispose( p[ pos ] ); |
| m_bundle.realloc( 0 ); |
| |
| Package::disposing(); |
| } |
| |
| // Package |
| //______________________________________________________________________________ |
| beans::Optional< beans::Ambiguous<sal_Bool> > |
| BackendImpl::PackageImpl::isRegistered_( |
| ::osl::ResettableMutexGuard &, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| { |
| //In case the object was created for a removed extension (m_bRemoved = true) |
| //but the extension is not registered, then bundle will be empty. Then |
| //the return value will be Optional<...>.IsPresent= false. Althoug this is |
| //not true, this does not matter. Then registerPackage or revokePackage |
| //would never be called for the items. But since the extension is removed |
| //and not registered anyway, this does not matter. |
| const Sequence< Reference<deployment::XPackage> > bundle( |
| getBundle( abortChannel.get(), xCmdEnv ) ); |
| |
| bool reg = false; |
| bool present = false; |
| bool ambig = false; |
| for ( sal_Int32 pos = bundle.getLength(); pos--; ) |
| { |
| Reference<deployment::XPackage> const & xPackage = bundle[ pos ]; |
| Reference<task::XAbortChannel> xSubAbortChannel( |
| xPackage->createAbortChannel() ); |
| AbortChannel::Chain chain( abortChannel, xSubAbortChannel ); |
| beans::Optional< beans::Ambiguous<sal_Bool> > option( |
| xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) ); |
| |
| //present = true if at least one bundle item has this value. |
| //reg = true if all bundle items have an option value (option.IsPresent == 1) |
| //and all have value of true (option.Value.Value == true) |
| //If not, then the bundle has the status of not registered and ambiguous. |
| if (option.IsPresent) |
| { |
| beans::Ambiguous<sal_Bool> const & status = option.Value; |
| if (present) |
| { |
| //we never come here in the first iteration |
| if (reg != (status.Value != sal_False)) { |
| |
| ambig = true; |
| reg = false; |
| break; |
| } |
| } |
| else |
| { |
| //we always come here in the first iteration |
| reg = status.Value; |
| present = true; |
| } |
| } |
| } |
| return beans::Optional< beans::Ambiguous<sal_Bool> >( |
| present, beans::Ambiguous<sal_Bool>(reg, ambig) ); |
| } |
| |
| OUString BackendImpl::PackageImpl::getTextFromURL( |
| const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv, |
| const OUString& licenseUrl) |
| { |
| try |
| { |
| ::ucbhelper::Content descContent(licenseUrl, xCmdEnv); |
| ::rtl::ByteSequence seq = dp_misc::readFile(descContent); |
| return OUString( reinterpret_cast<sal_Char const *>( |
| seq.getConstArray()), seq.getLength(), RTL_TEXTENCODING_UTF8); |
| } |
| catch (css::uno::Exception&) |
| { |
| Any exc( ::cppu::getCaughtException() ); |
| throw css::deployment::DeploymentException( |
| OUSTR("Could not read file ") + licenseUrl, 0, exc); |
| } |
| |
| } |
| |
| DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() |
| { |
| return dp_misc::getDescriptionInfoset(m_url_expanded); |
| } |
| |
| bool BackendImpl::PackageImpl::checkPlatform( |
| css::uno::Reference< css::ucb::XCommandEnvironment > const & environment) |
| { |
| bool ret = false; |
| DescriptionInfoset info(getDescriptionInfoset()); |
| Sequence<OUString> platforms(info.getSupportedPlaforms()); |
| if (hasValidPlatform(platforms)) |
| { |
| ret = true; |
| } |
| else |
| { |
| ret = false; |
| rtl::OUString msg( |
| RTL_CONSTASCII_USTRINGPARAM("unsupported platform")); |
| Any e( |
| css::deployment::PlatformException( |
| msg, static_cast<OWeakObject *>(this), this)); |
| if (!interactContinuation( |
| e, cppu::UnoType< css::task::XInteractionApprove >::get(), |
| environment, NULL, NULL)) |
| { |
| throw css::deployment::DeploymentException( |
| msg, static_cast<OWeakObject *>(this), e); |
| } |
| } |
| return ret; |
| } |
| |
| |
| bool BackendImpl::PackageImpl::checkDependencies( |
| css::uno::Reference< css::ucb::XCommandEnvironment > const & environment, |
| DescriptionInfoset const & description) |
| { |
| css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > > |
| unsatisfied(dp_misc::Dependencies::check(description)); |
| |
| if (unsatisfied.getLength() == 0) { |
| return true; |
| } else { |
| rtl::OUString msg( |
| RTL_CONSTASCII_USTRINGPARAM("unsatisfied dependencies")); |
| Any e( |
| css::deployment::DependencyException( |
| msg, static_cast<OWeakObject *>(this), unsatisfied)); |
| if (!interactContinuation( |
| e, cppu::UnoType< css::task::XInteractionApprove >::get(), |
| environment, NULL, NULL)) |
| { |
| throw css::deployment::DeploymentException( |
| msg, static_cast<OWeakObject *>(this), e); |
| } |
| return false; |
| } |
| } |
| |
| ::sal_Bool BackendImpl::PackageImpl::checkLicense( |
| css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv, |
| DescriptionInfoset const & info, bool alreadyInstalled) |
| throw (css::deployment::DeploymentException, |
| css::ucb::CommandFailedException, |
| css::ucb::CommandAbortedException, |
| css::uno::RuntimeException) |
| { |
| try |
| { |
| ::boost::optional<SimpleLicenseAttributes> simplLicAttr |
| = info.getSimpleLicenseAttributes(); |
| if (! simplLicAttr) |
| return true; |
| OUString sLic = info.getLocalizedLicenseURL(); |
| //If we do not get a localized licence then there is an error in the description.xml |
| //This should be handled by using a validating parser. Therefore we assume that no |
| //license is available. |
| if (sLic.getLength() == 0) |
| throw css::deployment::DeploymentException( |
| OUSTR("Could not obtain path to license. Possible error in description.xml"), 0, Any()); |
| OUString sHref = m_url_expanded + OUSTR("/") + sLic; |
| OUString sLicense = getTextFromURL(xCmdEnv, sHref); |
| ////determine who has to agree to the license |
| //check correct value for attribute |
| if ( ! (simplLicAttr->acceptBy.equals(OUSTR("user")) || simplLicAttr->acceptBy.equals(OUSTR("admin")))) |
| throw css::deployment::DeploymentException( |
| OUSTR("Could not obtain attribute simple-lincense@accept-by or it has no valid value"), 0, Any()); |
| |
| |
| //Only use interaction if there is no version of this extension already installed |
| //and the suppress-on-update flag is not set for the new extension |
| // alreadyInstalled | bSuppressOnUpdate | show license |
| //---------------------------------------- |
| // 0 | 0 | 1 |
| // 0 | 1 | 1 |
| // 1 | 0 | 1 |
| // 1 | 1 | 0 |
| |
| if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate)) |
| { |
| css::deployment::LicenseException licExc( |
| OUString(), 0, getDisplayName(), sLicense, |
| simplLicAttr->acceptBy); |
| bool approve = false; |
| bool abort = false; |
| if (! interactContinuation( |
| Any(licExc), task::XInteractionApprove::static_type(), xCmdEnv, &approve, &abort )) |
| throw css::deployment::DeploymentException( |
| OUSTR("Could not interact with user."), 0, Any()); |
| |
| if (approve == true) |
| return true; |
| else |
| return false; |
| //throw css::deployment::DeploymentException( |
| // OUSTR("Extension Manager: User declined the license."), |
| // static_cast<OWeakObject*>(this), |
| // Any( css::deployment::LicenseException(OUSTR("User declined the license."), 0, m_name, sLicense))); |
| } |
| return true; |
| } catch (css::ucb::CommandFailedException&) { |
| throw; |
| } catch (css::ucb::CommandAbortedException&) { |
| throw; |
| } catch (css::deployment::DeploymentException&) { |
| throw; |
| } catch (css::uno::RuntimeException&) { |
| throw; |
| } catch (css::uno::Exception&) { |
| Any anyExc = cppu::getCaughtException(); |
| throw css::deployment::DeploymentException(OUSTR("Unexpected exception"), 0, anyExc); |
| } |
| } |
| |
| ::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites( |
| const css::uno::Reference< css::task::XAbortChannel >&, |
| const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv, |
| sal_Bool alreadyInstalled) |
| throw (css::deployment::DeploymentException, |
| css::deployment::ExtensionRemovedException, |
| css::ucb::CommandFailedException, |
| css::ucb::CommandAbortedException, |
| css::uno::RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| DescriptionInfoset info = getDescriptionInfoset(); |
| if (!info.hasDescription()) |
| return 0; |
| |
| //always return LICENSE as long as the user did not accept the license |
| //so that XExtensonManager::checkPrerequisitesAndEnable will again |
| //check the license |
| if (!checkPlatform(xCmdEnv)) |
| return deployment::Prerequisites::PLATFORM | |
| deployment::Prerequisites::LICENSE; |
| else if(!checkDependencies(xCmdEnv, info)) |
| return deployment::Prerequisites::DEPENDENCIES | |
| deployment::Prerequisites::LICENSE; |
| else if(!checkLicense(xCmdEnv, info, alreadyInstalled)) |
| return deployment::Prerequisites::LICENSE; |
| else |
| return 0; |
| } |
| |
| ::sal_Bool BackendImpl::PackageImpl::checkDependencies( |
| const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv ) |
| throw (deployment::DeploymentException, |
| deployment::ExtensionRemovedException, |
| ucb::CommandFailedException, |
| RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| DescriptionInfoset info = getDescriptionInfoset(); |
| if (!info.hasDescription()) |
| return sal_True; |
| |
| return checkDependencies(xCmdEnv, info); |
| } |
| |
| beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier() |
| throw (RuntimeException) |
| { |
| OUString identifier; |
| if (m_bRemoved) |
| identifier = m_identifier; |
| else |
| identifier = dp_misc::generateIdentifier( |
| getDescriptionInfoset().getIdentifier(), m_name); |
| |
| return beans::Optional<OUString>( |
| true, identifier); |
| } |
| |
| OUString BackendImpl::PackageImpl::getVersion() |
| throw (deployment::ExtensionRemovedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| return getDescriptionInfoset().getVersion(); |
| } |
| |
| Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs() |
| throw (deployment::ExtensionRemovedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| return getDescriptionInfoset().getUpdateInformationUrls(); |
| } |
| |
| beans::StringPair BackendImpl::PackageImpl::getPublisherInfo() |
| throw (deployment::ExtensionRemovedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| ::std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL(); |
| beans::StringPair aStrPair( aInfo.first, aInfo.second ); |
| return aStrPair; |
| } |
| |
| //______________________________________________________________________________ |
| uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast ) |
| throw (deployment::ExtensionRemovedException, RuntimeException ) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| |
| uno::Reference< graphic::XGraphic > xGraphic; |
| |
| OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast ); |
| if ( aIconURL.getLength() ) |
| { |
| OUString aFullIconURL = m_url_expanded + OUSTR("/") + aIconURL; |
| |
| uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() ); |
| uno::Reference< graphic::XGraphicProvider > xGraphProvider( |
| xContext->getServiceManager()->createInstanceWithContext( OUSTR( "com.sun.star.graphic.GraphicProvider" ), xContext ), |
| uno::UNO_QUERY ); |
| |
| if ( xGraphProvider.is() ) |
| { |
| uno::Sequence< beans::PropertyValue > aMediaProps( 1 ); |
| aMediaProps[0].Name = OUSTR( "URL" ); |
| aMediaProps[0].Value <<= aFullIconURL; |
| |
| xGraphic = xGraphProvider->queryGraphic( aMediaProps ); |
| } |
| } |
| |
| return xGraphic; |
| } |
| |
| //______________________________________________________________________________ |
| void BackendImpl::PackageImpl::processPackage_( |
| ::osl::ResettableMutexGuard &, |
| bool doRegisterPackage, |
| bool startup, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| { |
| const Sequence< Reference<deployment::XPackage> > bundle( |
| getBundle( abortChannel.get(), xCmdEnv ) ); |
| |
| if (doRegisterPackage) |
| { |
| ExtensionBackendDb::Data data; |
| const sal_Int32 len = bundle.getLength(); |
| for ( sal_Int32 pos = 0; pos < len; ++pos ) |
| { |
| checkAborted(abortChannel); |
| Reference<deployment::XPackage> const & xPackage = bundle[ pos ]; |
| Reference<task::XAbortChannel> xSubAbortChannel( |
| xPackage->createAbortChannel() ); |
| AbortChannel::Chain chain( abortChannel, xSubAbortChannel ); |
| try { |
| xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv ); |
| } |
| catch (Exception &) |
| { |
| //We even try a rollback if the user cancelled the action (CommandAbortedException) |
| //in order to prevent invalid database entries. |
| Any exc( ::cppu::getCaughtException() ); |
| // try to handle exception, notify: |
| bool approve = false, abort = false; |
| if (! interactContinuation( |
| Any( lang::WrappedTargetException( |
| OUSTR("bundle item registration error!"), |
| static_cast<OWeakObject *>(this), exc ) ), |
| task::XInteractionApprove::static_type(), xCmdEnv, |
| &approve, &abort )) { |
| OSL_ASSERT( !approve && !abort ); |
| if (m_legacyBundle) // default for legacy packages: ignore |
| continue; |
| // no selection at all, so rethrow; |
| // no C++ rethrow after getCaughtException(), |
| // see cppuhelper/exc_hlp.hxx: |
| ::cppu::throwException(exc); |
| } |
| if (approve && !abort) // ignore error, just continue |
| continue; |
| |
| { |
| ProgressLevel progress( |
| xCmdEnv, OUSTR("rollback...") ); |
| // try rollback |
| for ( ; pos--; ) |
| { |
| try { |
| bundle[ pos ]->revokePackage( |
| xSubAbortChannel, xCmdEnv ); |
| } |
| catch (Exception &) |
| { |
| OSL_ENSURE( 0, ::rtl::OUStringToOString( |
| ::comphelper::anyToString( |
| ::cppu::getCaughtException() ), |
| RTL_TEXTENCODING_UTF8 ).getStr() ); |
| // ignore any errors of rollback |
| } |
| } |
| progress.update( OUSTR("rollback finished.") ); |
| } |
| |
| deployment::DeploymentException dpExc; |
| if (exc >>= dpExc) { |
| throw ucb::CommandFailedException( |
| dpExc.Message, dpExc.Context, dpExc.Cause ); |
| } |
| else { |
| // rethrow CommandFailedException |
| ::cppu::throwException(exc); |
| } |
| } |
| data.items.push_back( |
| ::std::make_pair(xPackage->getURL(), |
| xPackage->getPackageType()->getMediaType())); |
| } |
| getMyBackend()->addDataToDb(getURL(), data); |
| } |
| else |
| { |
| // revoke in reverse order: |
| for ( sal_Int32 pos = bundle.getLength(); pos--; ) |
| { |
| checkAborted(abortChannel); |
| Reference<deployment::XPackage> const & xPackage = bundle[ pos ]; |
| Reference<task::XAbortChannel> xSubAbortChannel( |
| xPackage->createAbortChannel() ); |
| AbortChannel::Chain chain( abortChannel, xSubAbortChannel ); |
| try { |
| bundle[ pos ]->revokePackage( xSubAbortChannel, xCmdEnv ); |
| } |
| catch (RuntimeException &) { |
| throw; |
| } |
| catch (ucb::CommandAbortedException &) { |
| throw; |
| } |
| catch (Exception &) { |
| // CommandFailedException, DeploymentException: |
| Any exc( ::cppu::getCaughtException() ); |
| // try to handle exception, notify: |
| bool approve = false, abort = false; |
| if (! interactContinuation( |
| Any( lang::WrappedTargetException( |
| OUSTR("bundle item revocation error!"), |
| static_cast<OWeakObject *>(this), exc ) ), |
| task::XInteractionApprove::static_type(), xCmdEnv, |
| &approve, &abort )) { |
| OSL_ASSERT( !approve && !abort ); |
| if (m_legacyBundle) // default for legacy packages: ignore |
| continue; |
| // no selection at all, so rethrow |
| // no C++ rethrow after getCaughtException(), |
| // see cppuhelper/exc_hlp.hxx: |
| ::cppu::throwException(exc); |
| } |
| // ignore errors when revoking, although abort may have been |
| // selected |
| } |
| } |
| getMyBackend()->revokeEntryFromDb(getURL()); |
| } |
| } |
| |
| //______________________________________________________________________________ |
| OUString BackendImpl::PackageImpl::getDescription() |
| throw (deployment::ExtensionRemovedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| |
| const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL()); |
| OUString sDescription; |
| if (sRelativeURL.getLength()) |
| { |
| OUString sURL = m_url_expanded + OUSTR("/") + sRelativeURL; |
| |
| try |
| { |
| sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL ); |
| } |
| catch ( css::deployment::DeploymentException& ) |
| { |
| OSL_ENSURE( 0, ::rtl::OUStringToOString( ::comphelper::anyToString( ::cppu::getCaughtException() ), RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| } |
| |
| if (sDescription.getLength()) |
| return sDescription; |
| return m_oldDescription; |
| } |
| |
| //______________________________________________________________________________ |
| OUString BackendImpl::PackageImpl::getLicenseText() |
| throw (deployment::ExtensionRemovedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| |
| OUString sLicense; |
| DescriptionInfoset aInfo = getDescriptionInfoset(); |
| |
| ::boost::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes(); |
| if ( aSimplLicAttr ) |
| { |
| OUString aLicenseURL = aInfo.getLocalizedLicenseURL(); |
| |
| if ( aLicenseURL.getLength() ) |
| { |
| OUString aFullURL = m_url_expanded + OUSTR("/") + aLicenseURL; |
| sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL); |
| } |
| } |
| |
| return sLicense; |
| } |
| |
| //______________________________________________________________________________ |
| void BackendImpl::PackageImpl::exportTo( |
| OUString const & destFolderURL, OUString const & newTitle, |
| sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| throw (ucb::CommandFailedException, |
| deployment::ExtensionRemovedException, |
| ucb::CommandAbortedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| |
| ::ucbhelper::Content sourceContent( m_url_expanded, xCmdEnv ); |
| OUString title(newTitle); |
| if (title.getLength() == 0) |
| sourceContent.getPropertyValue( StrTitle::get() ) >>= title; |
| OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode( |
| title, rtl_UriCharClassPchar, |
| rtl_UriEncodeIgnoreEscapes, |
| RTL_TEXTENCODING_UTF8 ) ) ); |
| |
| if (nameClashAction == ucb::NameClash::ASK) |
| { |
| if (create_ucb_content( |
| 0, destURL, xCmdEnv, false /* no throw */ )) { |
| bool replace = false, abort = false; |
| if (! interactContinuation( |
| Any( ucb::NameClashResolveRequest( |
| OUSTR("file already exists: ") + title, |
| static_cast<OWeakObject *>(this), |
| task::InteractionClassification_QUERY, |
| destFolderURL, title, OUString() ) ), |
| ucb::XInteractionReplaceExistingData::static_type(), xCmdEnv, |
| &replace, &abort ) || !replace) { |
| return; |
| } |
| } |
| } |
| else if (nameClashAction != ucb::NameClash::OVERWRITE) { |
| throw ucb::CommandFailedException( |
| OUSTR("unsupported nameClashAction!"), |
| static_cast<OWeakObject *>(this), Any() ); |
| } |
| erase_path( destURL, xCmdEnv ); |
| |
| ::rtl::OUStringBuffer buf; |
| buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.zip://") ); |
| buf.append( ::rtl::Uri::encode( destURL, |
| rtl_UriCharClassRegName, |
| rtl_UriEncodeIgnoreEscapes, |
| RTL_TEXTENCODING_UTF8 ) ); |
| buf.append( static_cast<sal_Unicode>('/') ); |
| OUString destFolder( buf.makeStringAndClear() ); |
| |
| ::ucbhelper::Content destFolderContent( destFolder, xCmdEnv ); |
| { |
| // transfer every item of folder into zip: |
| Reference<sdbc::XResultSet> xResultSet( |
| sourceContent.createCursor( |
| Sequence<OUString>(), |
| ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) ); |
| ProgressLevel progress( xCmdEnv, OUString() ); |
| while (xResultSet->next()) |
| { |
| ::ucbhelper::Content subContent( |
| Reference<ucb::XContentAccess>( |
| xResultSet, UNO_QUERY_THROW )->queryContent(), xCmdEnv ); |
| if (! destFolderContent.transferContent( |
| subContent, ::ucbhelper::InsertOperation_COPY, |
| OUString(), ucb::NameClash::OVERWRITE )) |
| throw RuntimeException( OUSTR("UCB transferContent() failed!"), |
| static_cast<OWeakObject *>(this) ); |
| progress.update( Any() ); // animating progress bar |
| } |
| } |
| |
| // assure META-INF folder: |
| ::ucbhelper::Content metainfFolderContent; |
| create_folder( &metainfFolderContent, |
| makeURL( destFolderContent.getURL(), OUSTR("META-INF") ), |
| xCmdEnv ); |
| |
| if (m_legacyBundle) |
| { |
| // easy to migrate legacy bundles to new format: |
| // just export them once using a .oxt name! |
| // set detected media-types of any bundle item: |
| |
| // collect all manifest entries: |
| Sequence< Reference<deployment::XPackage> > bundle; |
| try { |
| bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv ); |
| } |
| // xxx todo: think about exception specs: |
| catch (deployment::DeploymentException &) { |
| OSL_ENSURE( 0, ::rtl::OUStringToOString( |
| ::comphelper::anyToString( |
| ::cppu::getCaughtException() ), |
| RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| catch (lang::IllegalArgumentException & exc) { |
| (void) exc; |
| OSL_ENSURE( 0, ::rtl::OUStringToOString( |
| exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| |
| ::std::vector< Sequence<beans::PropertyValue> > manifest; |
| manifest.reserve( bundle.getLength() ); |
| sal_Int32 baseURLlen = m_url_expanded.getLength(); |
| Reference<deployment::XPackage> const *pbundle = bundle.getConstArray(); |
| const OUString strMediaType = OUSTR("MediaType"); |
| const OUString strFullPath = OUSTR("FullPath"); |
| const OUString strIsFolder = OUSTR("IsFolder"); |
| for ( sal_Int32 pos = bundle.getLength(); pos--; ) |
| { |
| Reference<deployment::XPackage> const & xPackage = pbundle[ pos ]; |
| OUString url_( expandUnoRcUrl( xPackage->getURL() ) ); |
| OSL_ASSERT( url_.getLength() >= baseURLlen ); |
| OUString fullPath; |
| if (url_.getLength() > baseURLlen) |
| fullPath = url_.copy( baseURLlen + 1 ); |
| ::ucbhelper::Content ucbContent( url_, xCmdEnv ); |
| if (ucbContent.getPropertyValue(strIsFolder).get<bool>()) |
| fullPath += OUSTR("/"); |
| Sequence<beans::PropertyValue> attribs( 2 ); |
| beans::PropertyValue * pattribs = attribs.getArray(); |
| pattribs[ 0 ].Name = strFullPath; |
| pattribs[ 0 ].Value <<= fullPath; |
| pattribs[ 1 ].Name = strMediaType; |
| const Reference<deployment::XPackageTypeInfo> xPackageType( |
| xPackage->getPackageType() ); |
| OUString mediaType; |
| OSL_ASSERT( xPackageType.is() ); |
| if (xPackageType.is()) |
| mediaType = xPackageType->getMediaType(); |
| else |
| mediaType = OUSTR("unknown"); |
| pattribs[ 1 ].Value <<= mediaType; |
| manifest.push_back( attribs ); |
| } |
| |
| // write into pipe: |
| Reference<XComponentContext> xContext( |
| getMyBackend()->getComponentContext() ); |
| Reference<packages::manifest::XManifestWriter> xManifestWriter( |
| xContext->getServiceManager()->createInstanceWithContext( |
| OUSTR("com.sun.star.packages.manifest.ManifestWriter"), |
| xContext ), UNO_QUERY_THROW ); |
| Reference<io::XOutputStream> xPipe( |
| xContext->getServiceManager()->createInstanceWithContext( |
| OUSTR("com.sun.star.io.Pipe"), xContext ), UNO_QUERY_THROW ); |
| xManifestWriter->writeManifestSequence( |
| xPipe, comphelper::containerToSequence(manifest) ); |
| |
| // write buffered pipe data to content: |
| ::ucbhelper::Content manifestContent( |
| makeURL( metainfFolderContent.getURL(), OUSTR("manifest.xml") ), |
| xCmdEnv ); |
| manifestContent.writeStream( |
| Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ), |
| true /* replace existing */ ); |
| } |
| else |
| { |
| // overwrite manifest.xml: |
| ::ucbhelper::Content manifestContent; |
| if ( ! create_ucb_content( |
| &manifestContent, |
| makeURL( m_url_expanded, OUSTR("META-INF/manifest.xml") ), |
| xCmdEnv, false ) ) |
| { |
| OSL_ENSURE( 0, "### missing META-INF/manifest.xml file!" ); |
| return; |
| } |
| |
| if (! metainfFolderContent.transferContent( |
| manifestContent, ::ucbhelper::InsertOperation_COPY, |
| OUString(), ucb::NameClash::OVERWRITE )) |
| throw RuntimeException( OUSTR("UCB transferContent() failed!"), |
| static_cast<OWeakObject *>(this) ); |
| } |
| |
| // xxx todo: maybe obsolete in the future |
| try { |
| destFolderContent.executeCommand( OUSTR("flush"), Any() ); |
| } |
| catch (ucb::UnsupportedCommandException &) { |
| } |
| } |
| |
| //______________________________________________________________________________ |
| sal_Bool BackendImpl::PackageImpl::isBundle() throw (RuntimeException) |
| { |
| return true; |
| } |
| |
| //______________________________________________________________________________ |
| Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle( |
| Reference<task::XAbortChannel> const & xAbortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| throw (deployment::DeploymentException, |
| ucb::CommandFailedException, ucb::CommandAbortedException, |
| lang::IllegalArgumentException, RuntimeException) |
| { |
| Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle; |
| if (pBundle == 0) |
| { |
| t_packagevec bundle; |
| if (m_bRemoved) |
| { |
| bundle = getPackagesFromDb(xCmdEnv); |
| } |
| else |
| { |
| try { |
| if (m_legacyBundle) |
| { |
| // .zip legacy packages allow script.xlb, dialog.xlb in bundle |
| // root folder: |
| OUString mediaType; |
| // probe for script.xlb: |
| if (create_ucb_content( |
| 0, makeURL( m_url_expanded, OUSTR("script.xlb") ), |
| xCmdEnv, false /* no throw */ )) { |
| mediaType = OUSTR("application/vnd.sun.star.basic-library"); |
| } |
| // probe for dialog.xlb: |
| else if (create_ucb_content( |
| 0, makeURL( m_url_expanded, OUSTR("dialog.xlb") ), |
| xCmdEnv, false /* no throw */ )) |
| mediaType = OUSTR("application/vnd.sun.star." |
| "dialog-library"); |
| |
| if (mediaType.getLength() > 0) { |
| const Reference<deployment::XPackage> xPackage( |
| bindBundleItem( getURL(), mediaType, false, OUString(), |
| xCmdEnv ) ); |
| if (xPackage.is()) |
| bundle.push_back( xPackage ); |
| // continue scanning: |
| } |
| scanLegacyBundle( bundle, getURL(), |
| AbortChannel::get(xAbortChannel), xCmdEnv ); |
| } |
| else |
| { |
| // .oxt: |
| scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv ); |
| } |
| |
| } |
| catch (RuntimeException &) { |
| throw; |
| } |
| catch (ucb::CommandFailedException &) { |
| throw; |
| } |
| catch (ucb::CommandAbortedException &) { |
| throw; |
| } |
| catch (deployment::DeploymentException &) { |
| throw; |
| } |
| catch (Exception &) { |
| Any exc( ::cppu::getCaughtException() ); |
| throw deployment::DeploymentException( |
| OUSTR("error scanning bundle: ") + getURL(), |
| static_cast<OWeakObject *>(this), exc ); |
| } |
| } |
| |
| // sort: schema before config data, typelibs before components: |
| Sequence< Reference<deployment::XPackage> > ret( bundle.size() ); |
| Reference<deployment::XPackage> * pret = ret.getArray(); |
| sal_Int32 lower_end = 0; |
| sal_Int32 upper_end = ret.getLength(); |
| t_packagevec::const_iterator iPos( bundle.begin() ); |
| t_packagevec::const_iterator const iEnd( bundle.end() ); |
| for ( ; iPos != iEnd; ++iPos ) |
| { |
| const Reference<deployment::XPackageTypeInfo> xPackageType( |
| (*iPos)->getPackageType() ); |
| OSL_ASSERT( xPackageType.is() ); |
| if (xPackageType.is()) { |
| const OUString mediaType( xPackageType->getMediaType() ); |
| String type, subType; |
| INetContentTypeParameterList params; |
| if (INetContentTypes::parse( |
| mediaType, type, subType, ¶ms ) && |
| type.EqualsIgnoreCaseAscii("application") && |
| (subType.EqualsIgnoreCaseAscii( |
| "vnd.sun.star.uno-component") || |
| subType.EqualsIgnoreCaseAscii( |
| "vnd.sun.star.configuration-data"))) |
| { |
| --upper_end; |
| pret[ upper_end ] = *iPos; |
| continue; |
| } |
| } |
| pret[ lower_end ] = *iPos; |
| ++lower_end; |
| } |
| OSL_ASSERT( lower_end == upper_end ); |
| |
| const ::osl::MutexGuard guard( getMutex() ); |
| pBundle = m_pBundle; |
| if (pBundle == 0) { |
| m_bundle = ret; |
| pBundle = &m_bundle; |
| OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); |
| m_pBundle = pBundle; |
| } |
| } |
| else { |
| OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); |
| } |
| return *pBundle; |
| } |
| |
| inline bool isBundle_( OUString const & mediaType ) |
| { |
| // xxx todo: additional parsing? |
| return mediaType.getLength() > 0 && |
| (mediaType.matchIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM( |
| "application/vnd.sun.star.package-bundle") ) || |
| mediaType.matchIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM( |
| "application/vnd.sun.star.legacy-package-bundle") )); |
| } |
| |
| //______________________________________________________________________________ |
| Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem( |
| OUString const & url, OUString const & mediaType, |
| sal_Bool bRemoved, OUString const & identifier, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv, |
| bool notifyDetectionError ) |
| { |
| // ignore any nested bundles: |
| if (isBundle_(mediaType)) |
| return Reference<deployment::XPackage>(); |
| |
| Reference<deployment::XPackage>xPackage; |
| try { |
| xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage( |
| url, mediaType, bRemoved, identifier, xCmdEnv ) ); |
| OSL_ASSERT( xPackage.is() ); |
| } |
| catch (RuntimeException &) { |
| throw; |
| } |
| catch (ucb::CommandFailedException &) { |
| // ignore already handled error |
| } |
| catch (Exception &) { |
| const Any exc( ::cppu::getCaughtException() ); |
| if (notifyDetectionError || |
| !exc.isExtractableTo( |
| ::getCppuType( reinterpret_cast< |
| lang::IllegalArgumentException const *>(0) ) )) |
| { |
| interactContinuation( |
| Any( lang::WrappedTargetException( |
| OUSTR("bundle item error!"), |
| static_cast<OWeakObject *>(this), exc ) ), |
| task::XInteractionApprove::static_type(), xCmdEnv, 0, 0 ); |
| } |
| } |
| |
| if (xPackage.is()) { |
| const Reference<deployment::XPackageTypeInfo> xPackageType( |
| xPackage->getPackageType() ); |
| OSL_ASSERT( xPackageType.is() ); |
| // ignore any nested bundles: |
| if (xPackageType.is() && isBundle_( xPackageType->getMediaType() )) |
| xPackage.clear(); |
| } |
| return xPackage; |
| } |
| |
| //______________________________________________________________________________ |
| void BackendImpl::PackageImpl::scanBundle( |
| t_packagevec & bundle, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv ) |
| { |
| OSL_ASSERT( !m_legacyBundle ); |
| |
| ::ucbhelper::Content manifestContent; |
| if (! create_ucb_content( |
| &manifestContent, |
| makeURL( m_url_expanded, OUSTR("META-INF/manifest.xml") ), |
| xCmdEnv, false /* no throw */ )) |
| { |
| OSL_ENSURE( 0, "### missing META-INF/manifest.xml file!" ); |
| return; |
| } |
| |
| |
| const lang::Locale officeLocale = getOfficeLocale(); |
| OUString descrFile; |
| lang::Locale descrFileLocale; |
| |
| const Reference<XComponentContext> xContext( |
| getMyBackend()->getComponentContext() ); |
| Reference<packages::manifest::XManifestReader> xManifestReader( |
| xContext->getServiceManager()->createInstanceWithContext( |
| OUSTR("com.sun.star.packages.manifest.ManifestReader"), |
| xContext ), UNO_QUERY_THROW ); |
| const Sequence< Sequence<beans::PropertyValue> > manifestSeq( |
| xManifestReader->readManifestSequence( manifestContent.openStream() ) ); |
| const OUString packageRootURL( getURL() ); |
| for ( sal_Int32 pos = manifestSeq.getLength(); pos--; ) |
| { |
| OUString fullPath, mediaType; |
| Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ]; |
| for ( sal_Int32 i = attribs.getLength(); i--; ) |
| { |
| if (fullPath.getLength() > 0 && mediaType.getLength() > 0) |
| break; |
| if (attribs[i].Name.equalsAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("FullPath") )) |
| attribs[i].Value >>= fullPath; |
| else if (attribs[i].Name.equalsAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("MediaType") )) |
| attribs[i].Value >>= mediaType; |
| } |
| |
| if (fullPath.getLength() == 0 || mediaType.getLength() == 0 || |
| mediaType.equalsAsciiL( // opt: exclude common text/xml |
| RTL_CONSTASCII_STRINGPARAM("text/xml") )) |
| continue; |
| |
| String type, subType; |
| INetContentTypeParameterList params; |
| if (! INetContentTypes::parse( mediaType, type, subType, ¶ms )) |
| continue; |
| |
| INetContentTypeParameter const * param = params.find( |
| ByteString("platform") ); |
| if (param != 0 && !platform_fits( param->m_sValue )) |
| continue; |
| const OUString url( makeURL( packageRootURL, fullPath ) ); |
| |
| // check for bundle description: |
| if (type.EqualsIgnoreCaseAscii("application") && |
| subType.EqualsIgnoreCaseAscii( |
| "vnd.sun.star.package-bundle-description")) |
| { |
| // check locale: |
| param = params.find( ByteString("locale") ); |
| if (param == 0) { |
| if (descrFile.getLength() == 0) |
| descrFile = url; |
| } |
| else { |
| // match best locale: |
| lang::Locale locale( toLocale(param->m_sValue) ); |
| if (locale.Language == officeLocale.Language) |
| { |
| if (descrFileLocale.Country == officeLocale.Country |
| && locale.Country != officeLocale.Country) |
| continue; |
| if (descrFileLocale.Variant == officeLocale.Variant |
| && locale.Variant != officeLocale.Variant) |
| continue; |
| descrFile = url; |
| descrFileLocale = locale; |
| } |
| } |
| continue; |
| } |
| |
| checkAborted( abortChannel ); |
| |
| //We make sure that we only create one XPackage for a particular URL. |
| //Sometime programmers insert the same URL several times in the manifest |
| //which may lead to DisposedExceptions. |
| if (bundle.end() == std::find_if(bundle.begin(), bundle.end(), XPackage_eq(url))) |
| { |
| const Reference<deployment::XPackage> xPackage( |
| bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) ); |
| if (xPackage.is()) |
| bundle.push_back( xPackage ); |
| } |
| else |
| { |
| fprintf(stderr, "manifest.xml contains a duplicate entry!\n"); |
| } |
| } |
| |
| if (descrFile.getLength() > 0) |
| { |
| ::ucbhelper::Content descrFileContent; |
| if (create_ucb_content( &descrFileContent, descrFile, |
| xCmdEnv, false /* no throw */ )) |
| { |
| // patch description: |
| ::rtl::ByteSequence bytes( readFile( descrFileContent ) ); |
| ::rtl::OUStringBuffer buf; |
| if ( bytes.getLength() ) |
| { |
| buf.append( OUString( reinterpret_cast<sal_Char const *>( |
| bytes.getConstArray() ), |
| bytes.getLength(), RTL_TEXTENCODING_UTF8 ) ); |
| } |
| else |
| { |
| buf.append( Package::getDescription() ); |
| } |
| m_oldDescription = buf.makeStringAndClear(); |
| } |
| } |
| } |
| |
| //______________________________________________________________________________ |
| void BackendImpl::PackageImpl::scanLegacyBundle( |
| t_packagevec & bundle, |
| OUString const & url, |
| ::rtl::Reference<AbortChannel> const & abortChannel, |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv, |
| bool skip_registration ) |
| { |
| ::ucbhelper::Content ucbContent( url, xCmdEnv ); |
| |
| // check for platform paths: |
| const OUString title( ucbContent.getPropertyValue( |
| StrTitle::get() ).get<OUString>() ); |
| if (title.endsWithIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM(".plt") ) && |
| !platform_fits( title.copy( 0, title.getLength() - 4 ) )) { |
| return; |
| } |
| if (title.endsWithIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("skip_registration") )) |
| skip_registration = true; |
| |
| OUString ar [] = { StrTitle::get(), OUSTR("IsFolder") }; |
| Reference<sdbc::XResultSet> xResultSet( |
| ucbContent.createCursor( |
| Sequence<OUString>( ar, ARLEN(ar) ), |
| ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) ); |
| while (xResultSet->next()) |
| { |
| checkAborted( abortChannel ); |
| |
| const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW ); |
| const OUString title_enc( ::rtl::Uri::encode( |
| xRow->getString( 1 /* Title */ ), |
| rtl_UriCharClassPchar, |
| rtl_UriEncodeIgnoreEscapes, |
| RTL_TEXTENCODING_UTF8 ) ); |
| const OUString path( makeURL( url, title_enc ) ); |
| |
| OUString mediaType; |
| const Reference<deployment::XPackage> xPackage( |
| bindBundleItem( path, OUString() /* detect */, false, OUString(), |
| xCmdEnv, false /* ignore detection errors */ ) ); |
| if (xPackage.is()) { |
| const Reference<deployment::XPackageTypeInfo> xPackageType( |
| xPackage->getPackageType() ); |
| OSL_ASSERT( xPackageType.is() ); |
| if (xPackageType.is()) |
| mediaType = xPackageType->getMediaType(); |
| |
| if (skip_registration && |
| // xxx todo: additional parsing? |
| mediaType.matchIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM( |
| "application/vnd.sun.star.uno-component") )) |
| continue; |
| |
| bundle.push_back( xPackage ); |
| } |
| |
| if (mediaType.getLength() == 0 || |
| // script.xlb, dialog.xlb can be met everywhere: |
| mediaType.matchIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM( |
| "application/vnd.sun.star.basic-library") ) || |
| mediaType.matchIgnoreAsciiCaseAsciiL( |
| RTL_CONSTASCII_STRINGPARAM( |
| "application/vnd.sun.star.dialog-library") )) |
| { |
| if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder: |
| scanLegacyBundle( |
| bundle, path, abortChannel, xCmdEnv, skip_registration ); |
| } |
| } |
| } |
| } |
| |
| OUString BackendImpl::PackageImpl::getDisplayName() |
| throw (deployment::ExtensionRemovedException, RuntimeException) |
| { |
| if (m_bRemoved) |
| throw deployment::ExtensionRemovedException(); |
| |
| OUString sName = getDescriptionInfoset().getLocalizedDisplayName(); |
| if (sName.getLength() == 0) |
| return m_displayName; |
| else |
| return sName; |
| } |
| |
| ::std::vector<Reference<deployment::XPackage> > |
| BackendImpl::PackageImpl::getPackagesFromDb( |
| Reference<ucb::XCommandEnvironment> const & xCmdEnv) |
| { |
| ::std::vector<Reference<deployment::XPackage> > retVector; |
| |
| typedef ::std::vector< ::std::pair<OUString, OUString> >::const_iterator ITC; |
| for (ITC i = m_dbData.items.begin(); i != m_dbData.items.end(); i++) |
| { |
| Reference<deployment::XPackage> xExtension = |
| bindBundleItem(i->first, i->second, true, m_identifier, xCmdEnv); |
| OSL_ASSERT(xExtension.is()); |
| retVector.push_back(xExtension); |
| } |
| |
| return retVector; |
| } |
| |
| } // anon namespace |
| |
| //============================================================================== |
| Reference<deployment::XPackageRegistry> create( |
| Reference<deployment::XPackageRegistry> const & xRootRegistry, |
| OUString const & context, OUString const & cachePath, bool readOnly, |
| Reference<XComponentContext> const & xComponentContext ) |
| { |
| Sequence<Any> args( |
| cachePath.getLength() == 0 ? 1 : 3 ); |
| args[ 0 ] <<= context; |
| if (cachePath.getLength() > 0) { |
| args[ 1 ] <<= cachePath; |
| args[ 2 ] <<= readOnly; |
| } |
| return new BackendImpl( args, xComponentContext, xRootRegistry ); |
| } |
| |
| } // namespace bundle |
| } // namespace backend |
| } // namespace dp_registry |
| |