/**************************************************************
 * 
 * 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, useing 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, &params ))
    {
        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, &params ) &&
                    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, &params ))
            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 pathes:
    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

