ncdengine/provider/server/src/ncdinstalloperationimpl.cpp
changeset 0 ba25891c3a9e
child 1 c42dffbd5b4f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ncdengine/provider/server/src/ncdinstalloperationimpl.cpp	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,1171 @@
+/*
+* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  
+*
+*/
+
+
+#include "ncdinstalloperationimpl.h"
+
+#include <s32mem.h>
+#include <f32file.h>
+#include <bautils.h>
+
+#include "catalogsbasemessage.h"
+#include "ncdnodemanager.h"
+#include "ncdnodeimpl.h"
+#include "ncdnodeidentifier.h"
+#include "catalogsutils.h"
+#include "catalogscontext.h"
+#include "ncdproviderdefines.h"
+#include "ncdnodeclassids.h"
+#include "catalogsconstants.h"
+#include "ncdinstallinfo.h"
+#include "ncdfileinfo.h"
+#include "ncdpurchasehistorydbimpl.h"
+#include "ncdutils.h"
+#include "ncderrors.h"
+#include "ncdoperationremovehandler.h"
+#include "ncdnodeinstallimpl.h"
+#include "ncdnodedownloadimpl.h"
+#include "ncdnodecontentinfoimpl.h"
+#include "ncdnodemetadataimpl.h"
+#include "ncdnodelink.h"
+#include "ncdproviderutils.h"
+#include "ncdpurchasehistoryutils.h"
+#include "ncdinstallreportobserver.h"
+#include "catalogsaccesspointmanager.h"
+#include "catalogsconnectionmethod.h"
+#include "catalogshttpconnectionmanager.h"
+#include "ncdgeneralmanager.h"
+
+#include "catalogsdebug.h"
+
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// NewL
+// ---------------------------------------------------------------------------
+//
+CNcdInstallOperation* CNcdInstallOperation::NewL(
+    MNcdOperationRemoveHandler& aRemoveHandler,
+    const CNcdNodeIdentifier& aNodeId,
+    CNcdGeneralManager& aGeneralManager,
+    MCatalogsHttpSession& aHttpSession,
+    MNcdInstallReportObserver& aReportObserver,
+    MCatalogsSession& aSession )
+    {
+    CNcdInstallOperation* self = new( ELeave ) CNcdInstallOperation( 
+        aRemoveHandler,
+        aGeneralManager,
+        aHttpSession,        
+        aReportObserver,
+        aSession );
+    CleanupClosePushL( *self );
+    self->ConstructL( aNodeId );
+
+    CleanupStack::Pop();
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Destructor
+// ---------------------------------------------------------------------------
+//
+CNcdInstallOperation::~CNcdInstallOperation()
+    {
+    DLTRACEIN((""));
+    
+    delete iNodeId;
+    
+    // Deletes the JAD from disk and also deletes iJadFile
+    DeleteJad();
+    DLTRACEOUT((""));
+    }
+
+// ---------------------------------------------------------------------------
+// Node Id getter
+// ---------------------------------------------------------------------------
+//
+const CNcdNodeIdentifier& CNcdInstallOperation::NodeIdentifier() const
+    {
+    return *iNodeId;
+    }
+
+// ---------------------------------------------------------------------------
+// Cancel
+// ---------------------------------------------------------------------------
+//
+void CNcdInstallOperation::Cancel() 
+    {    
+    DLTRACEIN(( "" ));
+    
+    // Nothing to do here. Most of the install operation
+    // functionality is in proxy side.
+    
+    DLTRACEOUT(( "" ));
+    }
+
+
+// ---------------------------------------------------------------------------
+// Receive message
+// ---------------------------------------------------------------------------
+//
+void CNcdInstallOperation::ReceiveMessage( 
+        MCatalogsBaseMessage* aMessage,
+        TInt aFunctionNumber )
+    {
+    DLTRACEIN((""));
+
+    DASSERT( aMessage );
+    
+    DLINFO(( "Handle: %i, aFunctionNumber=%d",
+            aMessage->Handle(),
+            aFunctionNumber));
+
+    // Now, we can be sure that rest of the time iMessage exists.
+    // This member variable is set for the CounterPartLost function.
+    iMessage = aMessage;
+    
+    TRAPD( err, DoReceiveMessageL( aMessage, aFunctionNumber ) ); 
+    
+    if ( err != KErrNone ) 
+        {
+        DLINFO(( ( "Error: %d" ), err ));
+        CompleteMessage( iMessage, 
+            ENCDOperationMessageCompletionError,
+            err );
+        }
+
+    // Because the message should not be used after this, set it NULL.
+    // So, CounterPartLost function will know that no messages are
+    // waiting the response at the moment.
+    iMessage = NULL;
+
+    DLTRACEOUT((""));
+    }
+
+
+// ---------------------------------------------------------------------------
+// Counter part lost
+// ---------------------------------------------------------------------------
+//
+void CNcdInstallOperation::CounterPartLost( const MCatalogsSession& aSession )
+    {
+    DLTRACEIN((""));
+
+    // This function may be called whenever -- when the message is waiting
+    // response or when the message does not exist.
+    // iMessage may be NULL here, because in the end of the
+    // ReceiveMessage it is set to NULL. The life time of the message
+    // ends shortly after CompleteAndRelease is called.
+    if ( iMessage != NULL )
+        {
+        iMessage->CounterPartLost( aSession );
+        }    
+
+    DLTRACEOUT((""));    
+    }
+
+// ---------------------------------------------------------------------------
+// RunOperation
+// ---------------------------------------------------------------------------
+//
+TInt CNcdInstallOperation::RunOperation()
+    {
+    DLTRACEIN(( "Pending message: %X", iPendingMessage ));    
+    
+   
+    DLTRACEOUT((""));
+    return KErrNone;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Initializer
+// ---------------------------------------------------------------------------
+//
+TInt CNcdInstallOperation::Initialize()
+    {
+    DLTRACEIN( ( "" ) );
+
+    TRAPD( err,
+        {
+        DLTRACE(("Writing initialize response"));
+        CBufBase* buf = CBufFlat::NewL( KBufExpandSize );
+        CleanupStack::PushL( buf );
+
+        RBufWriteStream stream( *buf );
+        CleanupClosePushL( stream );
+         
+        // write completion code
+        stream.WriteInt32L( ENCDOperationMessageCompletionInit );        
+
+        // Get the file count from the node and return it to the proxy
+        // Notice here that the node has to have some kind of metadata
+        // in order the install operation to start.
+        DLINFO(("Get the metadata of the node"));
+        CNcdNode& node( iNodeManager->NodeL( *iNodeId ) );
+        CNcdNodeMetaData& metaData( node.NodeMetaDataL() );
+        DLINFO(("Get the install object of the metadata."));
+        CNcdNodeInstall& install( metaData.InstallL() );        
+        stream.WriteInt32L( install.DownloadedFiles().MdcaCount() );        
+        
+        CleanupStack::PopAndDestroy( &stream );        
+
+        DLINFO(("Create report"));
+        // Because everything has gone smoothly so far,
+        // create the install report for the server reports.
+        CreateReportL();        
+        
+        DLTRACE(("Response length: %i", buf->Size() ));
+
+        iPendingMessage->CompleteAndReleaseL( buf->Ptr(0), KErrNone );
+        iPendingMessage = NULL;
+        CleanupStack::PopAndDestroy( buf );
+        });
+    
+    if ( err != KErrNone ) 
+        {
+        DLTRACE( ( "Err: %d", err ) );
+        iPendingMessage->CompleteAndRelease( err );
+        iPendingMessage = NULL;
+        }
+    DLTRACEOUT(( "err: %d", err ));
+    return err;    
+    }
+
+
+// ---------------------------------------------------------------------------
+// Constructor
+// ---------------------------------------------------------------------------
+//
+CNcdInstallOperation::CNcdInstallOperation( 
+    MNcdOperationRemoveHandler& aRemoveHandler,    
+    CNcdGeneralManager& aGeneralManager,
+    MCatalogsHttpSession& aHttpSession,    
+    MNcdInstallReportObserver& aReportObserver,
+    MCatalogsSession& aSession ) : 
+    CNcdBaseOperation( aGeneralManager, &aRemoveHandler, EInstallOperation, aSession ),
+    iHttpSession( aHttpSession ),
+    iAccessPointManager( aGeneralManager.AccessPointManager() ),
+    iReportObserver( aReportObserver ),
+    iReportId( KNcdReportNotSupported )
+    {
+    }
+
+
+// ---------------------------------------------------------------------------
+// ConstructL
+// ---------------------------------------------------------------------------
+//
+void CNcdInstallOperation::ConstructL( const CNcdNodeIdentifier& aNodeId )
+    {
+    DLTRACEIN( ( "" ) );
+    // Call ConstructL for the base class
+    CNcdBaseOperation::ConstructL();
+    
+    // copy the identifier
+    iNodeId = CNcdNodeIdentifier::NewL( aNodeId );
+    
+    iInstallService = &CNcdProviderUtils::InstallationServiceL();
+    DLTRACEOUT(( "" ));
+    }    
+    
+
+// ---------------------------------------------------------------------------
+// Gets info for an installable file from the node
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::GetFileInfoL( MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    TInt nextFile = ReadFileIndexL( aMessage );
+    
+    CNcdNode& node( iNodeManager->NodeL( *iNodeId ) );
+    // Notice that in order the node to start install, the
+    // node has to have some kind of metadata
+    CNcdNodeMetaData& metaData( node.NodeMetaDataL() );
+    CNcdNodeDownload& nodeDl = metaData.DownloadL();
+    CNcdNodeInstall& install( metaData.InstallL() );
+    
+    // Check that file index is in the valid range
+    if ( nextFile < 0 || nextFile >= install.DownloadedFiles().MdcaCount() ) 
+        {
+        DLERROR(("Invalid index"));
+        User::Leave( KErrArgument );
+        }
+
+    // Get item purpose        
+    TUint completePurpose = 0;
+    TPtrC contentMime;
+    
+    DeleteJad();
+    
+    TRAPD( error, 
+        {
+        const CNcdNodeContentInfo& contentInfo = 
+            node.NodeMetaData()->ContentInfoL();
+        
+        completePurpose = contentInfo.Purpose();
+        contentMime.Set( contentInfo.MimeType() );
+        DLINFO(( _L("Content mime: %S"), &contentMime ));
+        });
+    
+    LeaveIfNotErrorL( error, KErrNotFound );    
+    
+    // By default, use unknown purpose since installer
+    // uses purpose for installing themes
+    TNcdItemPurpose purpose = ENcdItemPurposeUnknown;
+
+    // Check if the purpose includes theme or application
+    if ( completePurpose & ENcdItemPurposeTheme ) 
+        {
+        DLTRACE(("Purpose: Theme"));
+        purpose = ENcdItemPurposeTheme;
+        }
+    else if ( completePurpose & ENcdItemPurposeApplication 
+        || completePurpose & ENcdItemPurposeGame 
+        || completePurpose & ENcdItemPurposeScreensaver )
+        {
+        DLTRACE(("Purpose: Application"));
+        purpose = ENcdItemPurposeApplication;
+        }
+    
+    // Used to index downloadInfos.
+    TInt downloadInfoIndex = nextFile;        
+    
+    // This check used to be for handling rights objects that were not really
+    // rights objects, eg. a picture given in the rights uri but now it has
+    // been removed
+    if ( install.DownloadedFiles().MdcaCount() >
+         nodeDl.DownloadInfo().Count() )
+        {
+        DLERROR(("Too many downloaded files, leaving with KErrCorrupt"));
+        User::Leave( KErrCorrupt );
+        }
+    
+    DLTRACE(("File index: %d, downloadInfoIndex: %d", nextFile, downloadInfoIndex ));
+    CNcdFileInfo* fileInfo = NULL;
+
+    // Use normal install by default
+    CNcdInstallInfo::TNcdInstallType installType = 
+        CNcdInstallInfo::ENcdInstallNormal;
+        
+            
+    // Skip files that have not been downloaded or are already installed
+    // Files that are downloaded are not skipped, doesn't matter 
+    // if they are installed or not
+    while( nextFile < install.DownloadedFiles().MdcaCount() &&
+           ( !install.DownloadedFiles().MdcaPoint( nextFile ).Length() ||
+           // Check if file is missing and is installed, if it's missing
+           // and not installed, we give an error by failing the installation
+           // on proxy side when the file is missing
+            ( !BaflUtils::FileExists( CNcdProviderUtils::FileSession(), 
+                install.DownloadedFiles().MdcaPoint( nextFile ) ) && (                    
+            install.IsContentInstalledL( nextFile, EFalse ) >= ENcdApplicationInstalled ||
+            // unless the file is launcher application which we can happily skip over
+            IsOneOf( nodeDl.DownloadInfo()[ downloadInfoIndex ]->ContentUsage(),
+                MNcdPurchaseDownloadInfo::ELauncher,                
+                MNcdPurchaseDownloadInfo::ELauncherOpen,
+                MNcdPurchaseDownloadInfo::EConsumable ) ) ) ) )
+        {
+        DLTRACE(("Skipping installed files or not downloaded files"));            
+        nextFile++;    
+        downloadInfoIndex++;
+        }
+   
+    // We may have skipped over the last possible file so we have to 
+    // complete the operation
+    if ( nextFile == install.DownloadedFiles().MdcaCount() ) 
+        {
+        DLERROR(("Either nothing has been downloaded or everything is already installed"));
+        HandleAllFilesInstalledL( nextFile, aMessage );
+        DLTRACEOUT(("Operation complete because all files have been installed"));
+        return;
+        }
+        
+    // Shortcut to correct download info
+    MNcdPurchaseDownloadInfo* downloadInfo( 
+        nodeDl.DownloadInfo()[ downloadInfoIndex ] );       
+
+    // Set dependency flag that we know not to update dependency/launcher
+    iIsDependency = NcdPurchaseHistoryUtils::IsDependency( *downloadInfo );
+        
+    
+    DLINFO(( _L("Descriptor type: %S"), &downloadInfo->DescriptorType() ));
+    DLINFO(( _L("Descriptor name: %S"), &downloadInfo->DescriptorName() ));
+    DLINFO(( _L("Descriptor URI: %S"), &downloadInfo->DescriptorUri() ));
+                
+    
+    if ( downloadInfo->ContentMimeType().MatchF( 
+            KMimeTypeMatch1JavaApplication ) != KErrNotFound ||
+         downloadInfo->ContentMimeType().MatchF( 
+            KMimeTypeMatch2JavaApplication ) != KErrNotFound ||
+         contentMime.MatchF( 
+            KMimeTypeMatch1JavaApplication ) != KErrNotFound ||
+         contentMime.MatchF( 
+            KMimeTypeMatch2JavaApplication ) != KErrNotFound )
+
+        {
+        if ( downloadInfo->DescriptorType().CompareF( 
+            KDescriptorTypeJad ) == 0 )
+            {
+            DLINFO(("Java app with JAD"));
+            purpose = ENcdItemPurposeApplication;
+            installType = CNcdInstallInfo::ENcdInstallJad;                
+            }
+        else 
+            {            
+            DLINFO(("Java-application"));
+            // Ensure that purpose is correct
+            purpose = ENcdItemPurposeApplication;
+            installType = CNcdInstallInfo::ENcdInstallJar;
+            }
+        }
+    else if ( downloadInfo->ContentMimeType().MatchF(
+            KMimeTypeMatchJad ) != KErrNotFound ||
+        contentMime.MatchF( KMimeTypeMatchJad ) != KErrNotFound )
+        {
+        DLINFO(("Java app with JAD"));
+        purpose = ENcdItemPurposeApplication;        
+        installType = CNcdInstallInfo::ENcdInstallJad;                
+        }
+
+
+    DLTRACE(("Creating fileinfo"));
+    // Create file info for proxy-side installer
+    fileInfo = CNcdFileInfo::NewLC( 
+        install.DownloadedFiles().MdcaPoint( nextFile ), 
+        nodeDl.DownloadInfo()[ downloadInfoIndex ]->ContentMimeType(), 
+        purpose );
+
+    if ( installType == CNcdInstallInfo::ENcdInstallJad ) 
+        {
+        DLINFO(( _L("Descriptor type: %S"), &downloadInfo->DescriptorType() ));
+        DLINFO(( _L("Descriptor name: %S"), &downloadInfo->DescriptorName() ));
+        DLINFO(( _L("Descriptor URI: %S"), &downloadInfo->DescriptorUri() ));
+        DLINFO(( "Descriptor Data: %S", &downloadInfo->DescriptorData() ));
+        if ( downloadInfo->DescriptorData().Length() ) 
+            {
+            // This "if" is an ugly fix for embedded DD descriptors where
+            // the .dm file is given in downloaddetails 
+            if ( !(downloadInfo->DescriptorType() == KDescriptorTypeOdd &&
+                downloadInfo->ContentUri().Length() ) )
+                {
+                // Writing JAD to file on this side because proxies can't
+                // write to engine's private dir
+                DeletePtr( iJadFile );
+                iJadFile = 
+                    CNcdProviderUtils::InstallationServiceL().WriteJadL(
+                        fileInfo->FilePath(), downloadInfo->DescriptorData() );
+                }
+            }
+        else 
+            {
+            DLINFO(("No descriptor for JAD install"));                        
+            }
+        // Ensure that mime is JAD
+        fileInfo->SetMimeTypeL( KMimeTypeMatchJad );
+        }
+    
+        
+    CNcdInstallInfo* info = CNcdInstallInfo::NewL( fileInfo,
+        installType );
+    CleanupStack::Pop( fileInfo );
+    CleanupStack::PushL( info );
+    info->SetIndex( nextFile );    
+        
+    MCatalogsBaseMessage* message = &aMessage;
+    // Send the info back
+    TInt err = CompleteMessage( 
+        message, 
+        ENCDOperationMessageCompletionComplete,
+        *info, 
+        KErrNone );
+    
+    CleanupStack::PopAndDestroy( info );
+    DLTRACEOUT(("err: %d", err));
+    }
+
+
+// ---------------------------------------------------------------------------
+// Completes the message correctly when all files have already been installed
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::HandleAllFilesInstalledL( 
+    TInt aFinalIndex, MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    CNcdInstallInfo* info = CNcdInstallInfo::NewL( NULL,
+        CNcdInstallInfo::ENcdInstallNormal );            
+    CleanupStack::PushL( info );
+    info->SetIndex( aFinalIndex );    
+        
+    MCatalogsBaseMessage* message = &aMessage;
+    // Send the info back
+    TInt err = CompleteMessage( 
+        message, 
+        ENCDOperationMessageCompletionComplete,
+        *info, 
+        KErrNone );
+    CleanupStack::PopAndDestroy( info );
+    DLTRACEOUT(("Err: %d", err));
+    }
+
+
+// ---------------------------------------------------------------------------
+// Updates the info of an installed file
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::UpdateInstalledFileInfoL( 
+    MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    
+    RCatalogsMessageReader reader;
+    reader.OpenLC( aMessage );
+        
+    // Read input message
+    DLTRACE(("Reading file index"));
+    TInt index = reader().ReadInt32L();
+
+    // Read the info from the stream
+    CNcdFileInfo* info = CNcdFileInfo::NewLC( reader() );
+    
+    // Read uids of installed applications
+    TInt appUids = reader().ReadInt32L();
+    DLTRACE(( "Reading app uids: %d", appUids ));
+    RArray<TUid> uids;
+    CleanupClosePushL( uids );
+    uids.ReserveL( appUids );
+    while( appUids ) 
+        {
+        uids.AppendL( TUid::Uid( reader().ReadInt32L() ) );
+        DLINFO(("Read UID: %x", uids[uids.Count() - 1] ));
+        --appUids;
+        }
+    
+    
+    // Installed dependencies/upgrades are not updated to purchase history
+    if ( !iIsDependency ) 
+        {        
+        //update purchase history with the info         
+        UpdatePurchaseHistoryL( *info, uids, 
+            aMessage.Session().Context().FamilyId() );
+        }
+        
+    CleanupStack::PopAndDestroy( 3, &reader );   // uids, info, reader
+    
+    DLTRACE(("Updating node from purchase history"));
+    // Update node from the purchase history
+    iNodeManager->InstallHandlerL( *iNodeId );
+        
+    DLTRACE(("Completing message"));
+    MCatalogsBaseMessage* message = &aMessage;
+    TInt err = CompleteMessage( 
+        message, 
+        ENCDOperationMessageCompletionComplete,
+        KErrNone );
+    
+    DLTRACEOUT((""));
+    }
+
+
+void CNcdInstallOperation::CreateReportL()
+    {
+    DLTRACEIN((""));  
+    CNcdNode& node( iNodeManager->NodeL( *iNodeId ) ); 
+    CNcdNodeMetaData& metaData( node.NodeMetaDataL() );
+    
+    TNcdReportStatusInfo info( ENcdReportCreate, KErrNone );
+    // Use the node identifier to identify the content in install report.
+    // Node id uniquely identifies the node that contains contents
+    // that will be installed. One node may contains multiple contents but
+    // they are all thought as one bundle, in one operation. Also, notice that 
+    // multiple nodes can contain same metadata and same content.
+
+    /**
+     * @ Get timestamp and purchase option id from purchase details and
+     * set them to the report
+     */
+    iReportId = 
+        iReportObserver.RegisterInstallL( 
+            iNodeId->NodeId(),
+            metaData.Identifier(),
+            info,
+            metaData.Identifier().ServerUri(),
+            metaData.Identifier().NodeNameSpace() );
+
+    // Access point is set when report is started.
+    // Then base message is used to get required info.
+    }
+
+
+void CNcdInstallOperation::StartReportL( MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    // Set access point for report.
+    UpdateReportAccessPointL( aMessage.Session().Context().FamilyId() );    
+
+    TNcdReportStatusInfo info( ENcdReportStart, KErrNone );
+    iReportObserver.ReportInstallStatusL(
+            iReportId,
+            info );
+
+    // If this leaves, ReceiveMessge will complete the message.
+    aMessage.CompleteAndRelease( KErrNone );
+    }
+    
+    
+void CNcdInstallOperation::CompleteReportL( MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    RCatalogsMessageReader reader;
+    reader.OpenLC( aMessage );
+    
+    RReadStream& stream( reader() );
+    TInt errorCode( stream.ReadInt32L() );
+        
+    CleanupStack::PopAndDestroy( &reader );        
+
+    TNcdReportStatus status( ENcdReportSuccess );
+    if ( errorCode == KErrNone )
+        {
+        status = ENcdReportSuccess;
+        }
+    else if ( errorCode == KErrCancel )
+        {
+        status = ENcdReportCancel;
+        }
+    else 
+        {
+        status = ENcdReportFail;
+        }
+    
+    // Create the status info object with the given info.
+    TNcdReportStatusInfo info( status, errorCode );
+    
+    iReportObserver.ReportInstallStatusL(
+            iReportId,
+            info );
+
+    // If this leaves, ReceiveMessge will complete the message.
+    aMessage.CompleteAndRelease( KErrNone );
+    }
+
+
+// ---------------------------------------------------------------------------
+// UpdateReportAccessPointL
+// ---------------------------------------------------------------------------
+//
+void CNcdInstallOperation::UpdateReportAccessPointL( const TUid& aClientUid )
+    {
+    DLTRACEIN((""));
+
+    CNcdPurchaseHistoryDb& db = iNodeManager->PurchaseHistory();    
+    CNcdNode& node( iNodeManager->NodeL( *iNodeId ) ); 
+    CNcdNodeMetaData& metadata( node.NodeMetaDataL() );
+    
+    CNcdPurchaseDetails* purchase = 
+        NcdPurchaseHistoryUtils::PurchaseDetailsLC(
+            db,
+            aClientUid,
+            metadata.Identifier(),
+            EFalse );
+                
+    // Create origin identifier
+    CNcdNodeIdentifier* originIdentifier = 
+        CNcdNodeIdentifier::NewL(
+            node.Identifier().NodeNameSpace(), 
+            purchase->OriginNodeId(), 
+            node.Identifier().ClientUid() );
+
+    CleanupStack::PopAndDestroy( purchase );
+    
+    CleanupStack::PushL( originIdentifier );
+    
+    // Get report ap    
+    TUint32 apId( 0 );
+
+    TInt error = 
+        iAccessPointManager.AccessPointIdL(
+            *originIdentifier, 
+            MCatalogsAccessPointManager::EBrowse, 
+            aClientUid, 
+            apId );
+        
+    TCatalogsConnectionMethod reportAp;
+    if ( error == KErrNone ) 
+        {
+        DLTRACE(( "Setting access point %d for reports", apId ))   
+        reportAp = 
+            TCatalogsConnectionMethod( 
+                apId, 
+                ECatalogsConnectionMethodTypeAccessPoint );
+        }
+    
+    if ( reportAp.iId == 0 ) 
+        {
+        reportAp = iHttpSession.ConnectionManager().DefaultConnectionMethod();
+        }
+
+    CleanupStack::PopAndDestroy( originIdentifier );
+
+    iReportObserver.SetInstallReportAccessPoint( 
+        iReportId,
+        reportAp );
+    }
+
+
+// ---------------------------------------------------------------------------
+// Updates purchase history
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::UpdatePurchaseHistoryL( const CNcdFileInfo& aInfo,
+    const RArray<TUid>& aAppUids, const TUid& aClientUid )
+    {
+    DLTRACEIN((""));
+    
+    CNcdPurchaseHistoryDb& db = iNodeManager->PurchaseHistory();    
+    CNcdNode& node( iNodeManager->NodeL( *iNodeId ) ); 
+    CNcdNodeMetaData& metadata( node.NodeMetaDataL() );
+    
+    CNcdPurchaseDetails* purchase = NcdPurchaseHistoryUtils::PurchaseDetailsLC(
+        db,
+        aClientUid,
+        metadata.Identifier(),
+        EFalse );
+                
+    
+    CNcdPurchaseInstallInfo* installInfo = CNcdPurchaseInstallInfo::NewLC();    
+       
+    const CNcdNodeContentInfo* contentInfo = NULL;                
+    TRAPD( err, contentInfo = &metadata.ContentInfoL() );            
+
+    LeaveIfNotErrorL( err, KErrNotFound );
+    
+    if ( aAppUids.Count() ) 
+        {
+        DLTRACE(("Updating installed application info"));
+        // Use application UID from the protocol if it exists
+        if ( contentInfo && contentInfo->Uid() != TUid::Null() ) 
+            {
+            DLINFO(( "Using UID from Content info: %x", 
+                contentInfo->Uid().iUid ));
+            installInfo->SetApplicationUid( contentInfo->Uid() );
+            }
+        else
+            {                
+            DLINFO(( "Using UID from installer: %x", aAppUids[0].iUid ));
+            installInfo->SetApplicationUid( aAppUids[0] );
+            }
+        
+        // Theme version number's are not updated from sis registry
+        if ( aInfo.Purpose() != ENcdItemPurposeTheme &&
+             aInfo.FilePath().Length() ) 
+            {
+            DLTRACE(( _L("Got version number from sis registry: %S"), 
+                &aInfo.FilePath() ));
+                
+            installInfo->SetApplicationVersionL(
+                aInfo.FilePath() );
+            }
+        else if ( contentInfo ) 
+            {        
+            DLTRACE(("Content info exists"));        
+            // Use application version from the protocol
+            DLINFO(( _L("Setting app version: %S"), 
+                &contentInfo->Version() ));
+            installInfo->SetApplicationVersionL( 
+                contentInfo->Version() );
+            }
+            
+        }
+    else if ( aInfo.Purpose() == ENcdItemPurposeTheme )
+        {
+        DLTRACE(("Updating installed theme info"));
+        installInfo->SetThemeNameL( aInfo.FilePath() );
+        }
+    else
+        {
+        DLTRACE(("Updating installed content info"));
+        DLINFO(( _L("Installed file: %S"), &aInfo.FilePath() ));
+        
+        // Save the filename 
+        installInfo->SetFilenameL( aInfo.FilePath() );
+        }
+    
+    // update purpose to purchase history
+    
+    // if purpose in content info was unknown, we use the one gotten from installer
+    // since it may have been updated to something more specific
+    if ( contentInfo && contentInfo->Purpose() != ENcdItemPurposeUnknown )     
+        {        
+        TUint newPurpose = contentInfo->Purpose();
+        if ( aInfo.Purpose() == ENcdItemPurposeApplication ) 
+            {
+            // This ensures that games etc. stuff that were installed like 
+            // applications, are also handled like applications 
+            newPurpose |= ENcdItemPurposeApplication;
+            }
+        DLTRACE(("Setting purpose as %d", newPurpose));
+        purchase->SetItemPurpose( newPurpose );
+        }
+    else
+        {        
+        // backup in case we didn't get content info for some reason
+        DLINFO(("Didn't get ContentInfo, updating purpose from FileInfo"));
+        purchase->SetItemPurpose( purchase->ItemPurpose() | aInfo.Purpose() );
+        }
+    
+    DLINFO(("Item purpose: %d", purchase->ItemPurpose() ));
+    // Add install info to purchase details. Ownership is transferred
+    
+    if ( ReplaceInstallInfoL( *purchase, *installInfo ) )
+        {
+        CleanupStack::PopAndDestroy( installInfo );
+        }
+    else 
+        {
+        DLTRACE(("Adding install info to purchase details"));    
+        purchase->AddInstallInfoL( installInfo );        
+        CleanupStack::Pop( installInfo );
+        }
+
+    DLTRACE(("Saving purchase details"));                
+    db.SavePurchaseL( *purchase, EFalse );    
+    CleanupStack::PopAndDestroy( purchase );
+    
+    DLTRACEOUT((""));
+    }
+
+
+// ---------------------------------------------------------------------------
+// Checks if the details already contain a matching info
+// ---------------------------------------------------------------------------
+//        
+TBool CNcdInstallOperation::ReplaceInstallInfoL( 
+    MNcdPurchaseDetails& aDetails, 
+    const MNcdPurchaseInstallInfo& aInfo )
+    {
+    DLTRACEIN((""));
+    TArray< MNcdPurchaseInstallInfo* > installInfos( 
+        aDetails.InstallInfoL() );
+        
+    TParsePtrC path( aInfo.Filename() );
+    TPtrC installedFile( path.NameAndExt() );
+    DLTRACE(("Going through %d install infos", installInfos.Count() ));
+    for ( TInt i = 0; i < installInfos.Count(); ++i )
+        {
+        MNcdPurchaseInstallInfo& oldInfo( *installInfos[ i ] );
+        
+        if ( ( oldInfo.ApplicationUid() == aInfo.ApplicationUid() &&
+               oldInfo.ApplicationVersion() == aInfo.ApplicationVersion() &&
+               oldInfo.ThemeName() == aInfo.ThemeName() ) ||
+               // also replace empty infos, hopefully this doesn't break anything
+               // because this is needed in order to correctly handle cancelled
+               // theme installations or more spefically successful theme installations
+               // after a cancelled theme installation
+             ( oldInfo.ApplicationUid() == KNullUid && 
+               oldInfo.ApplicationVersion() == KNullDesC &&
+               // replace the old info if theme name is empty or if the old theme name
+               // is not empty but the new info is entirely empty == reinstalling a theme
+               ( oldInfo.ThemeName() == KNullDesC || 
+                ( aInfo.ApplicationUid() == KNullUid &&
+                  aInfo.ApplicationVersion() == KNullDesC &&
+                  aInfo.ThemeName() == KNullDesC ) ) ) )
+             
+            {
+            // Parse the filename from the file path
+            TParsePtrC oldPath( oldInfo.Filename() );
+            if ( oldPath.NameAndExt() == installedFile ) 
+                {                
+                DLTRACEOUT(("Info already exists"));
+                
+                // Update the filename in case the file has been installed
+                // to a different drive than before
+                CNcdPurchaseInstallInfo* modInfo = 
+                    static_cast<CNcdPurchaseInstallInfo*>( installInfos[i] );
+                
+                // This updates the theme name in the following case:
+                // 1. theme was already installed when bought and installed with the client
+                // 2. user uninstalled the theme
+                // 3. user downloaded and installed the theme again
+                if ( oldInfo.ThemeName() == KNullDesC ) 
+                    {
+                    modInfo->SetThemeNameL( aInfo.ThemeName() );
+                    }
+                modInfo->SetFilenameL( aInfo.Filename() );
+                return ETrue;
+                }
+            }                    
+        }
+    
+    // This tries to update an existing java app uid when we are reinstalling
+    // java apps    
+    TArray< MNcdPurchaseDownloadInfo* > downloadInfos(
+        aDetails.DownloadInfoL() );        
+        
+    if ( installInfos.Count() &&
+         aInfo.ApplicationUid() != KNullUid && 
+         downloadInfos.Count() >= installInfos.Count() )        
+        {   
+        TBool isJava = EFalse;
+        for ( TInt i = 0; i < downloadInfos.Count(); ++i )
+            {
+            if ( IsJava( downloadInfos[i]->ContentMimeType(), ETrue ) )
+                {
+                isJava = ETrue;
+                break;
+                }
+            }
+        
+        if ( !isJava ) 
+            {
+            DLTRACEOUT(("No java app"));
+            return EFalse;
+            }
+            
+        DLTRACE(("Update the uid of the first existing app"));
+        for ( TInt i = 0; i < installInfos.Count(); ++i ) 
+            {
+            MNcdPurchaseInstallInfo& oldInfo( *installInfos[ i ] );
+            if ( oldInfo.ApplicationUid() != KNullUid ) 
+                {
+                DLTRACE(("Updating application uid from %x to %x",
+                    oldInfo.ApplicationUid(), aInfo.ApplicationUid() ));
+                // Update the app uid for java app                
+                CNcdPurchaseInstallInfo* modInfo = 
+                    static_cast<CNcdPurchaseInstallInfo*>( installInfos[i] );
+                
+                modInfo->SetApplicationUid( aInfo.ApplicationUid() );
+                modInfo->SetApplicationVersionL( aInfo.ApplicationVersion() );
+                return ETrue;
+                }                
+            }
+        }
+        
+    return EFalse;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Checks if the given mime type matches a java app
+// ---------------------------------------------------------------------------
+//        
+TBool CNcdInstallOperation::IsJava( 
+    const TDesC& aMimeType, 
+    TBool aAcceptJad ) const
+    {
+    DLTRACEIN(( _L("aMimeType: %S"), &aMimeType ));
+    
+    TBool matches = aMimeType.MatchF( KMimeTypeMatch1JavaApplication ) != KErrNotFound ||
+                  aMimeType.MatchF( KMimeTypeMatch2JavaApplication ) != KErrNotFound;
+
+    if ( !matches && aAcceptJad ) 
+        {
+        matches = aMimeType.MatchF( KMimeTypeMatchJad ) != KErrNotFound;
+        }
+    DLTRACEOUT(("Matched: %d", matches));
+    return matches;    
+    }
+
+
+// ---------------------------------------------------------------------------
+// Gets the path to the file indexed by the message
+// ---------------------------------------------------------------------------
+//        
+HBufC* CNcdInstallOperation::FilePathLC( 
+    MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    TInt fileIndex = ReadFileIndexL( aMessage );
+    
+    CNcdNode& node( iNodeManager->NodeL( *iNodeId ) );
+    // Notice that in order the node to start install, the
+    // node has to have some kind of metadata
+    CNcdNodeMetaData& metaData( node.NodeMetaDataL() );    
+    CNcdNodeInstall& install( metaData.InstallL() );
+    
+    // Check that file index is in the valid range
+    if ( fileIndex < 0 || fileIndex >= install.DownloadedFiles().MdcaCount() ) 
+        {
+        DLERROR(("Invalid index: %d", fileIndex ));
+        User::Leave( KErrArgument );
+        }
+                    
+    DLTRACEOUT(( _L("Filepath %S from index: %d"), 
+        &install.DownloadedFiles().MdcaPoint( fileIndex ), fileIndex ));
+
+    return install.DownloadedFiles().MdcaPoint( fileIndex ).AllocLC();
+    }
+
+
+// ---------------------------------------------------------------------------
+// Opens a file
+// ---------------------------------------------------------------------------
+//  
+void CNcdInstallOperation::OpenFileL( 
+    MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    RFs fs;
+    User::LeaveIfError( fs.Connect() );
+    CleanupClosePushL( fs );
+    
+    DLTRACE(("Sharing the file server"));
+    User::LeaveIfError( fs.ShareProtected() );
+    
+    DLTRACE(("Trying to open the file"));
+    RFile file;
+    CleanupClosePushL( file );
+    
+    // Ugly hackfix for handling JADs
+    if ( iJadFile ) 
+        {
+        User::LeaveIfError( file.Open( fs, 
+            *iJadFile,
+            NcdProviderDefines::KNcdSharableFileOpenFlags ) );                        
+        }
+    else
+        {
+        HBufC* path = FilePathLC( aMessage ); 
+        User::LeaveIfError( file.Open( fs, 
+            *path,
+            NcdProviderDefines::KNcdSharableFileOpenFlags ) );                
+        CleanupStack::PopAndDestroy( path );
+        }
+    DLTRACE(("File open, transferring to client"));
+    
+    aMessage.CompleteAndReleaseL( fs, file );
+    
+    DLTRACE(("File transferred"));
+    CleanupStack::PopAndDestroy( 2, &fs ); // file, fs    
+    }
+
+
+// ---------------------------------------------------------------------------
+// Does the actual message handling
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::DoReceiveMessageL( 
+        MCatalogsBaseMessage* aMessage,
+        TInt aFunctionNumber )
+    {
+    DLTRACEIN((""));
+    switch ( aFunctionNumber )
+        {
+        case ENCDOperationFunctionGetData:
+            {
+            GetFileInfoL( *aMessage );
+            break;
+            }
+            
+        case ENCDOperationFunctionSetData:
+            {
+            UpdateInstalledFileInfoL( *aMessage );
+            break;
+            }
+
+        case ENcdOperationFunctionOpenFile:
+            {
+            OpenFileL( *aMessage );
+            break;
+            }
+
+        case ENcdOperationFunctionDeleteFile:
+            {
+            DeleteFileL( *aMessage );
+            break;
+            }
+
+        case ENCDOperationFunctionReportStart:
+            {
+            StartReportL( *aMessage );
+            break;
+            }
+
+        case ENCDOperationFunctionReportComplete:
+            {
+            CompleteReportL( *aMessage );
+            break;
+            }
+
+        default:
+            {
+            DLTRACE(("Calling baseclass"));
+            // Call implementation in the base class
+            CNcdBaseOperation::ReceiveMessage( aMessage, aFunctionNumber );            
+            break;
+            }            
+        } 
+    DLTRACEOUT((""));
+    }
+
+
+// ---------------------------------------------------------------------------
+// Deletes a file index by the message. Also JAD is deleted if necessary
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::DeleteFileL( MCatalogsBaseMessage& aMessage )
+    {
+    DLTRACEIN((""));
+    HBufC* path = FilePathLC( aMessage );
+    // Delete installed SIS/JAR/content file
+    User::LeaveIfError( iInstallService->DeleteFile( *path ) );      
+    
+    CleanupStack::PopAndDestroy( path );
+    // If we installed JAD+JAR we must delete JAD separately.
+    DeleteJad();
+    
+    aMessage.CompleteAndReleaseL( KErrNone, KErrNone );    
+    }
+
+
+// ---------------------------------------------------------------------------
+// Reads a file index from the message
+// ---------------------------------------------------------------------------
+//        
+TInt CNcdInstallOperation::ReadFileIndexL( MCatalogsBaseMessage& aMessage )
+    {
+    RCatalogsMessageReader reader;
+    reader.OpenLC( aMessage );
+    
+    // Read the requested file index
+    TInt fileIndex = reader().ReadInt32L();
+    CleanupStack::PopAndDestroy( &reader );
+    return fileIndex;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Deletes JAD from disk and the filename
+// ---------------------------------------------------------------------------
+//        
+void CNcdInstallOperation::DeleteJad()
+    {
+    DLTRACEIN((""));
+    
+    if ( iJadFile && iInstallService ) 
+        {        
+        // Error is ignored because we can't handle disk errors in 
+        // any special way and other errors will make themselves known
+        // anyway
+        iInstallService->DeleteFile( *iJadFile );              
+        }
+    
+    DeletePtr( iJadFile );
+    }