ncdengine/provider/server/src/ncdpurchaseoperationimpl.cpp
changeset 0 ba25891c3a9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ncdengine/provider/server/src/ncdpurchaseoperationimpl.cpp	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,1756 @@
+/*
+* Copyright (c) 2006-2008 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:   ?Description
+*
+*/
+
+
+#include "ncdpurchaseoperationimpl.h"
+
+#include <badesca.h>
+#include <s32mem.h>
+#include <exterror.h> // for KErrGsmSMSShortMessageTransferRejected
+
+#include "ncdoperationfunctionids.h"
+#include "catalogsbasemessage.h"
+#include "catalogssmssession.h"
+#include "catalogshttpincludes.h"
+#include "ncdrequestgenerator.h"
+#include "ncdrequestbase.h"
+#include "ncdrequestpurchase.h"
+#include "ncdrequestconfigurationdata.h"
+#include "ncdprotocol.h"
+#include "ncdparser.h"
+#include "ncdnodemanager.h"
+#include "ncdnodeidentifier.h"
+#include "ncdnodeimpl.h"
+#include "ncdnodemetadataimpl.h"
+#include "ncdnodecontentinfoimpl.h"
+#include "ncdparserfactory.h"
+#include "ncdpaymentmethod.h"
+#include "ncd_pp_purchaseinformationimpl.h"
+#include "ncd_pp_purchaseprocessedimpl.h"
+#include "ncd_pp_information.h"
+#include "ncd_pp_error.h"
+#include "ncd_cp_queryresponse.h"
+#include "ncd_cp_query.h"
+#include "ncd_cp_queryimpl.h"
+#include "ncd_cp_queryelement.h"
+#include "ncd_cp_detail.h"
+#include "ncdstring.h"
+
+#include "ncdpurchaseoptionimpl.h"
+
+#include "ncdpurchasehistorydbimpl.h"
+#include "ncdutils.h"
+#include "ncddelay.h"
+#include "ncdnodelink.h"
+#include "catalogsconstants.h"
+#include "catalogsutils.h"
+#include "ncd_pp_download.h"
+#include "ncd_pp_descriptor.h"
+#include "ncd_pp_rights.h"
+#include "ncdqueryimpl.h"
+#include "ncdqueryselectionitemimpl.h"
+#include "ncdnodeiconimpl.h"
+#include "ncdoperationremovehandler.h"
+#include "ncdnodedownloadimpl.h"
+#include "ncddownloadinfo.h"
+
+#include "ncdsubscriptionmanagerimpl.h"
+#include "ncderrors.h"
+#include "ncdsessionhandler.h"
+#include "ncdprotocoldefaultobserver.h"
+#include "ncdnodedependencyimpl.h"
+#include "ncdnodeupgradeimpl.h"
+
+#include "ncdproviderutils.h"
+#include "ncdengineconfiguration.h"
+#include "catalogscontext.h"
+#include "ncdsearchnodefolder.h"
+#include "ncdhttputils.h"
+#include "ncdgeneralmanager.h"
+
+#include "catalogsdebug.h"
+
+// ======== MEMBER FUNCTIONS ========
+
+// Resend after timer is not needed if iResendAfter is -1
+const TInt KResendAfterDisabled = -1;
+
+// ---------------------------------------------------------------------------
+// ?description_if_needed
+// ---------------------------------------------------------------------------
+//
+CNcdPurchaseOperationImpl* CNcdPurchaseOperationImpl::NewL(
+    const TDesC& aNameSpace,
+    const TDesC& aNodeId,
+    const TUid& aClientUid,
+    const TDesC& aPurchaseOptionId,
+    CNcdGeneralManager& aGeneralManager,
+    MCatalogsHttpSession& aHttpSession,
+    MCatalogsSmsSession& aSmsSession,
+    CNcdSubscriptionManager& aSubscriptionManager,
+    MNcdOperationRemoveHandler* aRemoveHandler,
+    MCatalogsSession& aSession )
+    {
+    CNcdPurchaseOperationImpl* self = 
+        CNcdPurchaseOperationImpl::NewLC(
+            aNameSpace,
+            aNodeId,
+            aClientUid,
+            aPurchaseOptionId,
+            aGeneralManager,
+            aHttpSession,
+            aSmsSession,
+            aSubscriptionManager,
+            aRemoveHandler,
+            aSession );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// ?description_if_needed
+// ---------------------------------------------------------------------------
+//
+CNcdPurchaseOperationImpl* CNcdPurchaseOperationImpl::NewLC(
+    const TDesC& aNameSpace,
+    const TDesC& aNodeId,
+    const TUid& aClientUid,
+    const TDesC& aPurchaseOptionId,
+    CNcdGeneralManager& aGeneralManager,
+    MCatalogsHttpSession& aHttpSession,
+    MCatalogsSmsSession& aSmsSession,
+    CNcdSubscriptionManager& aSubscriptionManager,
+    MNcdOperationRemoveHandler* aRemoveHandler,
+    MCatalogsSession& aSession )
+    {
+    CNcdPurchaseOperationImpl* self = 
+        new( ELeave ) CNcdPurchaseOperationImpl(
+            aGeneralManager,
+            aHttpSession,
+            aSmsSession,
+            aSubscriptionManager,
+            aRemoveHandler,
+            aSession );
+    CleanupClosePushL( *self );
+    self->ConstructL( aNameSpace, aNodeId, aClientUid, aPurchaseOptionId );
+    return self;
+    }
+    
+
+// ---------------------------------------------------------------------------
+// ?description_if_needed
+// ---------------------------------------------------------------------------
+//
+CNcdPurchaseOperationImpl::CNcdPurchaseOperationImpl(
+    CNcdGeneralManager& aGeneralManager,
+    MCatalogsHttpSession& aHttpSession,
+    MCatalogsSmsSession& aSmsSession,
+    CNcdSubscriptionManager& aSubscriptionManager,
+    MNcdOperationRemoveHandler* aRemoveHandler,
+    MCatalogsSession& aSession )
+    : CNcdBaseOperation( aGeneralManager, aRemoveHandler, EPurchaseOperation,
+        aSession ),
+      iAccessPointManager( aGeneralManager.AccessPointManager() ),
+      iPurchaseHistory( &aGeneralManager.PurchaseHistory() ),
+      iHttpSession( aHttpSession ),
+      iSmsSession( &aSmsSession ),
+      iProtocol( aGeneralManager.ProtocolManager() ),
+      iSubscriptionManager( aSubscriptionManager ),
+      iLocked( EFalse )
+    {
+    iPurchaseOperationState = EBegin;
+    
+    }
+    
+
+// ---------------------------------------------------------------------------
+// ?description_if_needed
+// ---------------------------------------------------------------------------
+//
+void CNcdPurchaseOperationImpl::ConstructL(
+    const TDesC& aNameSpace,
+    const TDesC& aNodeId,
+    const TUid& aClientUid,
+    const TDesC& aPurchaseOptionId )
+    {
+    DLTRACEIN((""));
+
+    CNcdBaseOperation::ConstructL();
+
+    iNodeIdentifier = CNcdNodeIdentifier::NewL( aNameSpace, aNodeId, aClientUid );
+
+    // Get the node where the purchase operation was started from
+    iNode = &iNodeManager->NodeL( *iNodeIdentifier );
+
+    // Get the selected purchase option
+    RPointerArray<CNcdPurchaseOptionImpl> purchaseOptions = 
+        iNode->NodeMetaDataL().PurchaseOptions();
+
+    for( TInt i = 0; i < purchaseOptions.Count(); i++ )
+        {
+        if( purchaseOptions[i]->Id().CompareF( aPurchaseOptionId ) == 0 )
+            {
+            iSelectedPurchaseOption = purchaseOptions[i];
+            }
+        }
+    
+    // Purchase option is needed so leave if not found    
+    if ( !iSelectedPurchaseOption )
+        {
+        User::Leave( KErrNotFound );
+        }
+    }
+
+
+// ---------------------------------------------------------------------------
+// ?description_if_needed
+// ---------------------------------------------------------------------------
+//
+CNcdPurchaseOperationImpl::~CNcdPurchaseOperationImpl()
+    {
+    delete iPurchaseOptionId;
+    delete iNodeIdentifier;
+    delete iParser;
+    if ( iHttpTransaction ) 
+        {
+        // Cancel also releases the operation
+        iHttpTransaction->Cancel();        
+        }
+    
+    delete iSmsRegistrationRequest;
+    delete iPurchaseInformationData;
+    delete iPurchaseProcessedData;
+    if( iPurchaseQuery )
+        {
+        iPurchaseQuery->InternalRelease();
+        }
+    delete iRedirectUri;
+    delete iDelay;
+    iPendingSmsOperations.Reset();
+    }
+
+// ---------------------------------------------------------------------------
+// Node Id getter
+// ---------------------------------------------------------------------------
+//
+const CNcdNodeIdentifier& CNcdPurchaseOperationImpl::NodeIdentifier() const
+    {
+    return *iNodeIdentifier;
+    }
+
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CNcdPurchaseOperationImpl::Cancel()
+    {
+    DLTRACEIN( ( "" ) );
+    if ( iHttpTransaction )
+        {
+        iHttpTransaction->Cancel();
+        iHttpTransaction = NULL;
+        }
+    
+    if ( iParser )
+        {
+        iParser->CancelParsing();        
+        }
+    
+    DeletePtr( iDelay );
+    
+    TInt i = iPendingSmsOperations.Count();
+    while( i-- )
+        {
+        iPendingSmsOperations[i]->Cancel();
+        // The previous cancel will result with current implementation
+        // in HandleSmsEvent() call. Although this happens release
+        // and removal is done here to be sure that they are done.
+        // (This way this function is not dependant whether the
+        // HandleSmsEvent callback occurs or not)                    
+	    iPendingSmsOperations[i]->Release();
+        iPendingSmsOperations.Remove( i );
+        }
+    	
+    }
+    
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CNcdPurchaseOperationImpl::HandleHttpEventL( 
+        MCatalogsHttpOperation& aOperation, 
+        TCatalogsHttpEvent aEvent )
+    {
+    DLTRACEIN((""));
+
+    DASSERT( aOperation.OperationType() == ECatalogsHttpTransaction );
+    
+    TInt err = KErrNone;
+    switch( aEvent.iOperationState )
+        {
+            
+        case ECatalogsHttpOpInProgress:
+            {
+            // Don't bother to send empty bodies because this event 
+            // is sent also for received headers etc.
+            if ( aOperation.Body().Length() )
+                {                
+                // Send received data to the parser
+                TRAP(err, iParser->ParseL( aOperation.Body() ));
+                }
+
+            break;
+            }
+
+        case ECatalogsHttpOpCompleted:
+            {
+            TRAP( err, iParser->EndL() );
+            break;
+            }
+        }
+
+    if( err != KErrNone )
+        {
+        iError = err;
+        iPurchaseOperationState = EFailed;
+        Cancel();
+        RunOperation();
+        }
+        
+    DLTRACEOUT((""));
+    }
+    
+
+
+
+// ---------------------------------------------------------------------------
+// From class CNcdBaseOperation.
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+TInt CNcdPurchaseOperationImpl::RunOperation()
+    {
+    DLTRACEIN((""));
+    
+    if ( !iPendingMessage )
+        {
+        DLTRACEOUT(("No pending message -> return"));
+        return KErrNone;
+        }
+
+    DLTRACE(("Purchase operation state: %d", iPurchaseOperationState));
+    
+    if ( iLocked ) 
+        {
+        DLINFO(("Operation locked"));
+        return KErrNone;
+        }
+    
+    TRAPD( err, DoRunOperationL() );
+     
+     if ( err != KErrNone )
+        {
+        DLTRACE(("error: %d", err));
+        Cancel();
+        iPurchaseOperationState = EFailed;
+        iError = err;
+        if ( iPendingMessage )
+            {
+            // error ignored because operation already failed
+            CNcdBaseOperation::CompleteMessage( iPendingMessage,
+                ENCDOperationMessageCompletionError, err );
+            }
+        }
+    DLTRACEOUT((""));
+    return err;
+    }
+
+
+void CNcdPurchaseOperationImpl::DoRunOperationL()
+    {
+    DLTRACEIN((""));
+    switch ( iPurchaseOperationState )
+        {
+        case EBegin:
+            {
+            DLTRACE( _L( "->EBegin" ) );
+            
+            // At first check if there is enough disk space for the downloadable
+            // content. Stop the purchase process if this is not the case.
+            TInt contentSize = 0;
+            TRAP_IGNORE( contentSize = iNode->NodeMetaDataL().ContentInfoL().TotalContentSize() );          
+            DLINFO((("contentSize=%d"), contentSize ));
+            if ( contentSize > 0 ) 
+                {                
+                HBufC* clientDataPath = CNcdProviderUtils::EngineConfig().ClientDataPathLC( 
+                    iGeneralManager.FamilyName(), EFalse );    
+                DLINFO(( _L("Client data path: %S"), clientDataPath )); 
+                WouldDiskSpaceGoBelowCriticalLevelL(
+                    *clientDataPath, CNcdProviderUtils::FileSession(), contentSize );
+                CleanupStack::PopAndDestroy( clientDataPath );                
+                }
+            
+            // Skip purchase process for free items that doesn't implicitely
+            // require going through it
+            // Notice that we don't support purchasing of a subscription
+            // that is free and does not require purchase process. Purchase
+            // process is used as no downloadinfo is received!!
+            if( iSelectedPurchaseOption->IsFree() && 
+                !iSelectedPurchaseOption->RequirePurchaseProcess() &&
+                iSelectedPurchaseOption->DownloadInfoCount() > 0 ) 
+                {
+                iPurchaseOperationState = EDownloadDetailsReceived;
+                
+                CNcdBaseOperation::CompleteMessage( 
+                    iPendingMessage, 
+                    ENCDOperationMessageCompletionProgress,
+                    iProgress,
+                    KErrNone );
+                }
+            else
+                {
+
+                // Create purchase request and send it. Next callback will be
+                // HandleHttpEventL or HandleHttpError.
+                HBufC8* purchaseRequest = CreatePurchaseRequestLC(
+                    EPurchaseRequest,
+                    iNode->NodeLinkL().ServerUri() );
+
+                SendRequestL( iNode->NodeLinkL().ServerUri(), *purchaseRequest );
+                CleanupStack::PopAndDestroy( purchaseRequest );
+
+                iPurchaseOperationState = EPurchaseRequestSent;
+                }
+            DLTRACE(( "EBegin done" ));
+            break;
+            }
+
+        case EPurchaseInformationReceived:
+            {
+            DLTRACE( _L( "->EPurchaseInformationReceived" ) );
+            
+            if( !iPurchaseInformationData )
+                {
+                User::Leave( KNcdErrorNoPurchaseInformationReceived );
+                }
+
+            // Handle redirection if server requested for it.
+            if( iPurchaseInformationData->Uri() != KNullDesC )
+                {
+                // store redirect uri
+                delete iRedirectUri;
+                iRedirectUri = NULL;
+                iRedirectUri = iPurchaseInformationData->Uri().AllocL();
+                
+                HBufC8* purchaseRequest;
+
+                // Redirection with new session
+                if( iPurchaseInformationData->InitiateSession() )
+                    {
+                    // remove session if there is one
+                    iProtocol.SessionHandlerL( iSession.Context() ).RemoveSession(
+                    iPurchaseInformationData->Uri(), iNodeIdentifier->NodeNameSpace() );
+                    purchaseRequest = 
+                        CreatePurchaseRequestLC( 
+                            EPurchaseRequest,
+                            iPurchaseInformationData->Uri() );
+                    }
+                
+                // Redirection without new session
+                else
+                    {
+                    purchaseRequest = 
+                        CreatePurchaseRequestLC( 
+                            EPurchaseRequest,
+                            iNode->NodeLinkL().ServerUri() ); 
+                    }
+                
+                SendRequestL( 
+                    *iRedirectUri, *purchaseRequest );
+                CleanupStack::PopAndDestroy( purchaseRequest );
+                iPurchaseOperationState = EPurchaseRequestResent;
+                }
+            else if( iPurchaseInformationData->Disclaimer() )
+                {
+                DLTRACE(("Sending purchase disclaimer"));
+                if ( iPurchaseQuery )
+                    {
+                    iPurchaseQuery->InternalRelease();
+                    iPurchaseQuery = NULL;
+                    }
+                iPurchaseQuery = 
+                    CNcdQuery::NewL( *iPurchaseInformationData->Disclaimer() );
+                CNcdBaseOperation::QueryReceivedL( iPurchaseQuery );
+                
+                iPurchaseOperationState = EDisclaimerQueried;
+                }
+            else
+                {
+                iPurchaseOperationState = EDisclaimerQueried;
+
+                // Send progress info to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage,
+                         ENCDOperationMessageCompletionProgress,
+                         iProgress,
+                         KErrNone );
+                }
+            DLTRACE(( "EPurchaseInformationReceived done" ));
+            break;
+            }
+            
+        case EDisclaimerQueried:
+            {
+            DLTRACE(( "EDisclaimerQueried" ));
+                      
+            // purchase option is free but payment methods exist
+            // -> there may be querys that need showing
+            if( iSelectedPurchaseOption->IsFree() &&
+                iPurchaseInformationData->PaymentCount() == 1 )
+                {
+                // automatically select the purchase option
+                iSelectedPaymentMethod = &iPurchaseInformationData->PaymentL( 0 );
+                // continue operation so that possible querys get shown
+                iPurchaseOperationState = EPaymentMethodsQueried;
+                
+                // Send progress info to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage,
+                         ENCDOperationMessageCompletionProgress,
+                         iProgress,
+                         KErrNone );
+                }
+            // purchase option is free but no payment methods exist
+            else if( iSelectedPurchaseOption->IsFree() &&
+                iPurchaseInformationData->PaymentCount() < 1 )
+                {
+                // send purchase confirmation next
+                iPurchaseOperationState = EPaymentProcessed;
+                // Send progress info to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage,
+                         ENCDOperationMessageCompletionProgress,
+                         iProgress,
+                         KErrNone );
+                }
+            // Send received payment methods to the proxy
+            else
+                {
+                // Set received price text to the purchase option.
+                iSelectedPurchaseOption->SetPriceTextL( 
+                    iPurchaseInformationData->EntityL( 0 ).PriceText() );
+
+                // Now that the purchase option has been updated (new price)
+                // on the server side, it has to be reinternalized into
+                // proxy side. This is done in
+                // CNcdPurchaseOperationProxy::QueryReceivedCallback.
+				//
+                // It might be nicer to pass the updated price
+                // some way in the query instead of updating the
+                // purchase option. This way the same updated price
+                // would not (in update situations) be in two places
+                // in purchase history (final price and the price of
+                // purchase option).
+
+
+                RPointerArray<CNcdString> paymentMethodNames( KListGranularity );
+                CleanupResetAndDestroyPushL( paymentMethodNames );
+
+                CDesC8ArrayFlat* paymentMethodTypes =
+                    new ( ELeave ) CDesC8ArrayFlat( KListGranularity );
+
+                CleanupStack::PushL( paymentMethodTypes );
+
+                HBufC8* paymentMethodType;
+
+                TInt count = iPurchaseInformationData->PaymentCount();
+                
+                // must have at least one payment method
+                if( count < 1 )
+                    {
+                    DLERROR(("No payment methods!"));
+                    DASSERT(0);
+                    User::Leave( KNcdErrorNoPurchaseInformation );
+                    }
+                
+                for( TInt i = 0; i < count; i++ )
+                    {
+                    CNcdString* paymentMethodName = CNcdString::NewLC( 
+                        iPurchaseInformationData->PaymentL( i ).Name() );
+                    paymentMethodNames.AppendL( paymentMethodName );
+                    CleanupStack::Pop( paymentMethodName );
+
+                    paymentMethodType = IntToDes8LC( iPurchaseInformationData->PaymentL( i ).Method() );
+                    paymentMethodTypes->AppendL( *paymentMethodType );
+                    CleanupStack::PopAndDestroy( paymentMethodType );
+                    }
+
+                
+                if ( iPurchaseQuery )
+                    {
+                    iPurchaseQuery->InternalRelease();
+                    iPurchaseQuery = NULL;
+                    }
+                iPurchaseQuery = 
+                    CNcdQuery::NewL( paymentMethodNames, *paymentMethodTypes );
+                CNcdBaseOperation::QueryReceivedL( iPurchaseQuery );
+                
+                CleanupStack::PopAndDestroy( paymentMethodTypes );
+                CleanupStack::PopAndDestroy( &paymentMethodNames );
+                iPurchaseOperationState = EPaymentMethodsQueried;
+
+                }
+            DLTRACE(( "EDisclaimerQueried done" ));
+            break;
+            }
+
+        case EPaymentMethodsQueried:
+            {
+            DLTRACE( _L( "->EPaymentMethodsQueried" ) );
+            
+            // Query exists for the selected payment method. 
+            if( iSelectedPaymentMethod->QueryId() != KNullDesC() )
+                {
+                if ( iPurchaseQuery )
+                    {
+                    iPurchaseQuery->InternalRelease();
+                    iPurchaseQuery = NULL;
+                    }
+                
+                TBool secureConnection = EFalse;
+                if ( iRedirectUri )
+                    {
+                    secureConnection = IsHttpsUri( *iRedirectUri );
+                    }
+                else
+                    {
+                    secureConnection = IsHttpsUri( iNode->NodeLinkL().ServerUri() );
+                    }
+                    
+                iPurchaseQuery = 
+                    CNcdQuery::NewL( CNcdBaseOperation::QueryEntityL( 
+                        iSelectedPaymentMethod->QueryId() ), secureConnection );
+                        
+                CNcdBaseOperation::QueryReceivedL( iPurchaseQuery );
+                
+                iPurchaseOperationState = EPaymentInformationQueried;
+                }
+
+            // No query available. Continue purchase process.
+            else
+                {
+                iPurchaseOperationState = EPaymentInformationQueried;
+
+                // Send progress info to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage, 
+                         ENCDOperationMessageCompletionProgress,
+                         iProgress,
+                         KErrNone );
+                }
+            DLTRACE(("EPaymentMethodsQueried done"));
+            break;
+            }
+
+        case EPaymentInformationQueried:
+            {
+            DLTRACE(("EPaymentInformationQueried"));
+            // Send payment SMSs if SMS payment method was selected.
+            if( iSelectedPaymentMethod->Method() == 
+                MNcdPaymentMethod::EPaymentSms
+                && iSelectedPaymentMethod->SmsDetailsCount() > 0 )
+                {
+                // Sms payments are processed if there is something to process.
+                // If sms details were not given, then this operation should be
+                // continued in the else clause below as in normal case.
+                ProcessSmsPaymentL();
+                }
+            else
+                {
+                iPurchaseOperationState = EPaymentProcessed;
+
+                // Send progress info to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage, 
+                         ENCDOperationMessageCompletionProgress,
+                         iProgress,
+                         KErrNone );
+                }
+            DLTRACE(("EPaymentInformationQueried"));
+            break;
+            }
+
+        case EPaymentProcessed:
+            {
+            DLTRACE( _L( "->EPaymentProcessed" ) );
+            
+            // Create purchase confirmation and send it.
+            
+            // Handle redirection if server requested for it.
+            if( iPurchaseInformationData->Uri() != KNullDesC )
+                {
+                DLTRACE(( "Redirection." ));
+                delete iRedirectUri;
+                iRedirectUri = NULL;
+                iRedirectUri = iPurchaseInformationData->Uri().AllocL();
+                
+                HBufC8* purchaseRequest;
+
+                // Redirection with new session
+                if( iPurchaseInformationData->InitiateSession() )
+                    {
+                    DLTRACE(( "Redirection with a new session." ));
+                    // remove session if there is one
+                    iProtocol.SessionHandlerL( iSession.Context() ).RemoveSession(
+                    iPurchaseInformationData->Uri(), iNodeIdentifier->NodeNameSpace() );
+                    purchaseRequest = 
+                        CreatePurchaseRequestLC( 
+                            EPurchaseConfirmation,
+                            iPurchaseInformationData->Uri() );
+                    }
+                
+                // Redirection without new session
+                else
+                    {
+                    DLTRACE(( "Redirection without a new session." ));
+                    purchaseRequest = 
+                        CreatePurchaseRequestLC( 
+                            EPurchaseConfirmation,
+                            iNode->NodeLinkL().ServerUri() ); 
+                    }
+                SendRequestL( iPurchaseInformationData->Uri(), *purchaseRequest );
+                CleanupStack::PopAndDestroy( purchaseRequest );
+                }
+            // previously redirected
+            else if ( iRedirectUri )
+                {
+                DLTRACE(( "Previously redirected" ));
+                HBufC8* purchaseRequest = 
+                    CreatePurchaseRequestLC( EPurchaseConfirmation, *iRedirectUri );
+                SendRequestL( *iRedirectUri, *purchaseRequest );
+                CleanupStack::PopAndDestroy( purchaseRequest );
+                }
+            // Otherwise use original server URI.
+            else
+                {
+                DLTRACE(( "No redirection, using original server URI." ));
+                HBufC8* purchaseRequest = 
+                    CreatePurchaseRequestLC( EPurchaseConfirmation, iNode->NodeLinkL().ServerUri() );
+                SendRequestL( iNode->NodeLinkL().ServerUri(), *purchaseRequest );
+                CleanupStack::PopAndDestroy( purchaseRequest );
+                }
+
+            iPurchaseOperationState = EPurchaseConfirmationSent;
+            DLTRACE(( "EPaymentProcessed done" ));
+            break;
+            }
+
+        case EDownloadDetailsReceived:
+            {
+            DLTRACE( _L( "->EDownloadDetailsReceived" ) );
+
+            MNcdPurchaseOption::TType selectedOptionType = 
+                iSelectedPurchaseOption->PurchaseOptionType();
+
+            if( selectedOptionType ==
+                    MNcdPurchaseOption::EPurchase ||
+                selectedOptionType ==
+                    MNcdPurchaseOption::ESubscriptionPurchase )
+                {
+                DLTRACE(("Purchase done normally or with subscription."));
+                
+                // Update purchase information to the purchase history and notify
+                // node to refresh itself.
+                UpdatePurchaseHistoryL();
+                
+                DLTRACE(("Purchase history updated."));
+                
+                // Update node from the purchase db
+                iNodeManager->PurchaseHandlerL( iNode->Identifier() );
+                
+                DLTRACE(("Node download internalized."));                
+                }
+                
+
+            iPurchaseOperationState = EPurchaseComplete;
+            
+            if( iPurchaseProcessedData && iPurchaseProcessedData->Information() )
+                {
+                if ( iPurchaseQuery )
+                    {
+                    iPurchaseQuery->InternalRelease();
+                    iPurchaseQuery = NULL;
+                    }
+                iPurchaseQuery = 
+                    CNcdQuery::NewL( *iPurchaseProcessedData->Information() );
+                CNcdBaseOperation::QueryReceivedL( iPurchaseQuery );
+                }
+            else 
+                {                
+                // Send info about operation completion to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage, 
+                         ENCDOperationMessageCompletionComplete,
+                         iProgress,
+                        KErrNone );
+                }
+            break;
+            }
+            
+        case EPurchaseComplete:
+            {
+            DLTRACE(("EPurchaseComplete"));
+            if ( iPendingMessage )
+                {                
+                // Send info about operation completion to the proxy
+                CNcdBaseOperation::CompleteMessage( iPendingMessage, 
+                         ENCDOperationMessageCompletionComplete,
+                         iProgress,
+                        KErrNone );
+                }
+            DLTRACE(("EPurchaseComplete done"));
+            break;
+            }
+        
+        case EFailed:
+            {
+            DLTRACE(( "->EFailed" ));
+            Cancel();
+            if ( iPendingMessage )                                 
+                {                
+                User::LeaveIfError( CNcdBaseOperation::CompleteMessage(
+                    iPendingMessage, ENCDOperationMessageCompletionError, iProgress, iError ) );
+                }
+            DLTRACE(("EFailed done"));
+            break;
+            }
+        
+        default:
+            {
+            // RunOperation should not be called for other states.
+            DASSERT( 0 );
+            }
+
+        }     
+    }
+
+
+void CNcdPurchaseOperationImpl::ChangeToPreviousStateL()
+    {
+    DLTRACEIN((""));
+    switch( iPurchaseOperationState )
+        {
+        case EPurchaseRequestSent:
+        case EPurchaseInformationReceived:
+            {
+            iPurchaseOperationState = EBegin;
+            break;
+            }
+        case EPurchaseRequestResent:
+            {
+            iPurchaseOperationState = EPurchaseInformationReceived;
+            break;
+            }
+        case EPurchaseConfirmationSent:
+            {
+            iPurchaseOperationState = EPaymentProcessed;
+            break;
+            }
+        default:
+            {
+            DLTRACE(("CAN'T GO BACK FROM THIS STATE: %d, ERROR!", iPurchaseOperationState));
+            DASSERT(0);
+            User::Leave( KErrArgument );
+            break;
+            }
+        }
+    }
+
+
+
+HBufC8* CNcdPurchaseOperationImpl::CreatePurchaseRequestLC( 
+    TPurchaseRequestType aType,
+    const TDesC& aServerUri )
+    {
+    DLTRACEIN((""));
+    // Create a purchase request
+    CNcdRequestPurchase* req =
+        NcdRequestGenerator::CreatePurchaseRequestLC();
+
+    req->SetNamespaceL( iNodeIdentifier->NodeNameSpace() );
+
+    // Fill necessary information to the purchase request.
+    switch( aType )
+        {
+        case EPurchaseRequest:
+            {
+            DLTRACE(("EPurchaseRequest"));
+            req->AddEntityDetailsL( iNode->NodeLinkL().MetaDataIdentifier().NodeId(),
+            						iNode->NodeMetaDataL().TimeStamp(),
+                                    iSelectedPurchaseOption->Id() );
+            DLTRACE(("EPurchaseRequest done"));
+            break;
+            }
+        case EPurchaseConfirmation:
+            {
+            DLTRACE(("EPurchaseConfirmation"));
+            // Set transaction id
+            if( iPurchaseInformationData->TransactionId() != KNullDesC() )
+            	{
+                DLTRACE(("set transaction id"));
+	            req->SetTransactionIdL( iPurchaseInformationData->TransactionId() );
+            	}
+            	
+            DLTRACE(("set purchase confirmation"));
+            if ( iSelectedPaymentMethod ) 
+                {
+                if( iPurchaseQuery )
+                    {
+                    DLTRACE(("adding query id"));
+                    req->SetPurchaseConfirmationL( iSelectedPaymentMethod->QueryId(),
+            							   iSelectedPaymentMethod->Method() );
+                    }
+                else
+                    {
+                    DLTRACE(("not adding query id"));
+                    req->SetPurchaseConfirmationL( iSelectedPaymentMethod->Method() );
+                    }
+                }
+            else 
+                {
+                req->SetPurchaseConfirmationL( KNullDesC, // free item -> no query id
+            							   MNcdPaymentMethod::EPaymentDirect );
+                }
+            
+            if( iPurchaseInformationData->EntityCount() > 0 &&
+                iPurchaseInformationData->EntityL( 0 ).Ticket() != KNullDesC )
+				{
+                DLTRACE(("add entity with ticket"));
+                req->AddPurchaseConfirmationEntityL( iNode->NodeLinkL().MetaDataIdentifier().NodeId(),
+                                                     iNode->NodeMetaDataL().TimeStamp(),
+                                                     iSelectedPurchaseOption->Id(),
+                                                     iPurchaseInformationData->EntityL( 0 ).Ticket() );
+				}
+            else
+				{
+                DLTRACE(("add entity"));
+                req->AddPurchaseConfirmationEntityL( iNode->NodeLinkL().MetaDataIdentifier().NodeId(),
+                                                     iNode->NodeMetaDataL().TimeStamp(),
+                                                     iSelectedPurchaseOption->Id() );
+				}
+		    DLTRACE(("EPurchaseConfirmation done"));
+            break;
+            }
+        }
+
+    
+    AddQueryResponsesL( req );
+    
+    if( iPurchaseQuery )
+        {
+        DLTRACE(("Adding query response"));
+        req->AddQueryResponseL( CreateResponseL( *iPurchaseQuery ) );
+        }
+    
+    HBufC8* data = iProtocol.ProcessPreminetRequestL( iSession.Context(), *req, aServerUri );
+    DLINFO( ( "Request: %S", data  ) );
+    CleanupStack::PopAndDestroy( req );
+    CleanupStack::PushL( data );
+    return data;
+    }
+
+
+void CNcdPurchaseOperationImpl::SendRequestL( const TDesC& aServerUri,
+                                              const TDesC8& aData )
+{
+    DLTRACEIN((""));
+	// Create a parser to handle the response
+	if( iParser )
+	    {
+	    iParser->CancelParsing();
+	    delete iParser;
+	    iParser = NULL;
+	    }
+	
+	iParser = iProtocol.CreateParserL( iSession.Context(), aServerUri );
+    
+    iParser->Observers().SetParserObserver( this );
+    iParser->Observers().SetPurchaseObserver( this );
+    iParser->Observers().SetQueryObserver( this );
+    iParser->Observers().SetInformationObserver( this );
+    iParser->Observers().SetErrorObserver( this );
+
+    iParser->BeginAsyncL();
+
+	
+    // Create a HTTP transaction
+    iGeneralManager.HttpUtils().CreateTransactionL( 
+        iHttpSession,
+        iHttpTransaction,
+        aServerUri,
+        *this,
+        aData,
+        *iNodeIdentifier,
+        MCatalogsAccessPointManager::EPurchase,
+        iNodeIdentifier->ClientUid() );
+    
+    // Start transaction
+    User::LeaveIfError( iHttpTransaction->Start() );    
+    }
+
+
+TBool CNcdPurchaseOperationImpl::HandleHttpError( 
+    MCatalogsHttpOperation& /* aOperation */, 
+    TCatalogsHttpError aError )
+    {
+    DLTRACEIN(("Error type: %d, code: %d", aError.iType, aError.iError ));
+    iError = aError.iError;
+    iPurchaseOperationState = EFailed;
+    Cancel();
+    RunOperation();
+    return ETrue;
+    }
+
+
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CNcdPurchaseOperationImpl::ParseError( TInt aErrorCode )
+    {
+    DLTRACEIN(("error:%d", aErrorCode ));
+    // Handle only if not handling an error already.
+    if( iError == KErrNone )
+        {
+        iPurchaseOperationState = EFailed;
+        iError = aErrorCode;
+        Cancel();
+        RunOperation();
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CNcdPurchaseOperationImpl::ParseCompleteL( TInt /*aError*/ )
+    {
+    DLTRACEIN((""));
+    
+    // Should the error cases be handled here or in the ParseError 
+    // callback? Probably not in both.
+    if ( iLocked ) 
+        {
+        // do not complete the message in case the operation is locked
+        return;
+        }
+    
+
+    switch( iPurchaseOperationState )
+        {
+        case EPurchaseRequestSent:
+        case EPurchaseRequestResent:
+            {
+            iPurchaseOperationState = EPurchaseInformationReceived;
+            break;
+            }
+        case EPurchaseConfirmationSent:
+            {
+            iPurchaseOperationState = EDownloadDetailsReceived;
+            break;
+            }
+        default:
+            {
+            DLERROR(( "Unexpected state!" ));
+            DASSERT( EFalse );
+            break;
+            }
+        }
+    
+    // Clear the completed queries from base op, so that we don't send them
+    // again in the next request.
+    ClearCompletedQueries();
+    
+    // handle querys if any, calls RunOperation after querys have been handled
+    HandleQuerysL();
+    }
+
+//
+void CNcdPurchaseOperationImpl::InformationL(
+    MNcdPreminetProtocolPurchaseInformation* aData )
+    {
+    delete iPurchaseInformationData;
+    iPurchaseInformationData = aData;
+    }
+
+void CNcdPurchaseOperationImpl::ProcessedL(
+    MNcdPreminetProtocolPurchaseProcessed* aData )
+    {
+    delete iPurchaseProcessedData;
+    iPurchaseProcessedData = aData;
+    if ( iPurchaseProcessedData->ResultCode() != 0 )
+        {
+        DLTRACE(("Error: %d, Stop operation!",
+            iPurchaseProcessedData->ResultCode() ));
+        iPurchaseOperationState = EFailed;
+        iError = iPurchaseProcessedData->ResultCode();
+        Cancel();
+        RunOperation();
+        }
+    }
+    
+void CNcdPurchaseOperationImpl::InformationL(
+    MNcdPreminetProtocolInformation* aData ) 
+    {
+    DLTRACEIN((""));
+    
+    // handle resend after
+    iResendAfter = aData->ResendAfter();
+    if ( iResendAfter > KResendAfterDisabled ) 
+        {
+        DLINFO(( "Resend after %d requested", iResendAfter ));
+        switch ( iPurchaseOperationState ) 
+            {
+            case EPurchaseRequestSent:
+                iPurchaseOperationState = EBegin;
+                break;
+                
+            case EPurchaseConfirmationSent:
+                iPurchaseOperationState = EPaymentProcessed;
+                break;
+                
+            default:
+                // Resend after should not be requested in other cases.
+                DASSERT( EFalse );
+                break;
+            }
+
+        StartResendAfterTimer();
+        }
+        
+    CNcdBaseOperation::InformationL( aData );    
+    }
+
+
+void CNcdPurchaseOperationImpl::ErrorL( MNcdPreminetProtocolError* aData ) 
+    {
+    DLTRACEIN((""));
+    // Map error codes to correct enumeration values.
+    
+    CleanupDeletePushL( aData );
+    
+    switch ( aData->Code() ) 
+        {
+        case 404:
+            {
+            iError = KNcdErrorNotFound;
+            iSubscriptionManager.RemoveSubscriptionL(
+                *iNodeIdentifier, *iPurchaseOptionId );
+            break;
+            }
+        case 416:
+            {
+            DLTRACE(("session expired"));
+            Cancel();
+            if( iPurchaseInformationData && iPurchaseInformationData->Uri() != KNullDesC )
+                {
+                iProtocol.SessionHandlerL( iSession.Context() ).RemoveSession( 
+                    iPurchaseInformationData->Uri(), iNodeIdentifier->NodeNameSpace() );
+                }
+            iProtocol.SessionHandlerL( iSession.Context() ).RemoveSession(
+                iNode->NodeLinkL().ServerUri(), iNodeIdentifier->NodeNameSpace() );
+            DLINFO(("Continue operation from previous state"));
+            ChangeToPreviousStateL();
+            // continue operation asynchronously to prevent problems with parser
+            ContinueOperationL();
+            break;
+            }
+        case 426:
+            iError = KNcdErrorSubscriptionPaymentAlreadyDone;
+            break;
+        case 427:
+            iError = KNcdErrorSubscriptionNotSubscribed;
+            iSubscriptionManager.RemoveSubscriptionL(
+                *iNodeIdentifier, *iPurchaseOptionId );
+            break;
+        case 428:
+            iError = KNcdErrorSubscriptionInvalid;
+            iSubscriptionManager.RemoveSubscriptionL(
+                *iNodeIdentifier, *iPurchaseOptionId );
+            break;
+        case 429:
+            iError = KNcdErrorSubscriptionNotEnoughCredits;
+            break;
+        default:
+            iError = KNcdProtocolErrorBase - aData->Code();
+            break;
+        }
+    
+    CleanupStack::Pop( aData );
+    
+    // Default observer deletes aData
+    iParser->DefaultObserver().ErrorL( aData );
+    
+    if ( iError != KErrNone )
+        {
+        iPurchaseOperationState = EFailed;
+        Cancel();
+        RunOperation();
+        }
+    }    
+
+
+void CNcdPurchaseOperationImpl::QueryL( MNcdConfigurationProtocolQuery* aData )
+    {
+    DLTRACEIN(("query: sem=%d", aData->Semantics()));    
+    // check if we need to do something special here,
+    // otherwise forward the query to the base operation
+    
+    if ( aData->Semantics() == MNcdQuery::ESemanticsRegistrationQuery ) 
+        {
+        DLTRACE(("received sms registration query"));
+        CleanupDeletePushL( aData );        
+        
+        TInt err( KErrNone );
+        TRAP( err, 
+        for ( TInt i = 0; i < aData->QueryElementCount(); ++i ) 
+            {
+            const MNcdConfigurationProtocolQueryElement& element = 
+                aData->QueryElementL(i);
+
+            if ( element.Type() == MNcdConfigurationProtocolQueryElement::ESms ) 
+                {
+                DLTRACE(("sms query element"));    
+                const MNcdConfigurationProtocolDetail* detail = 
+                    element.Detail();
+                _LIT(KValueSmsAddress, "smsAddress");
+                _LIT(KValueSmsMessage, "smsMessage");
+
+                delete iSmsRegistrationRequest;
+                iSmsRegistrationRequest = NULL;
+
+                const RPointerArray<MNcdConfigurationProtocolContent>& contents(
+                    detail->Contents() );
+
+                TInt addressKey = KErrNotFound;
+                TInt messageKey = KErrNotFound;
+                for ( TInt k = 0; k < contents.Count(); ++k ) 
+                    {
+                    MNcdConfigurationProtocolContent* content = contents[ k ];
+
+                    if ( content->Key() == KValueSmsAddress ) 
+                        {
+                        DLTRACE(( _L("got sms address: %S"), &content->Value() ));                            
+                        addressKey = k;
+                        }
+                    else if ( content->Key() == KValueSmsMessage ) 
+                        {
+                        DLTRACE(( _L("got sms message: %S"), &content->Value() ));    
+                        messageKey = k;
+                        }
+                    }
+
+                if ( addressKey != KErrNotFound &&
+                     messageKey != KErrNotFound )
+                    {
+                    iSmsRegistrationRequest = CNcdKeyValuePair::NewL(
+                        contents[ addressKey ]->Value(),
+                        contents[ messageKey ]->Value() );
+                    DLTRACE(("sms address and messager ok"));    
+                        
+                    // both values are now set, create query
+                    CNcdConfigurationProtocolQueryImpl* cpQuery = 
+                        CNcdConfigurationProtocolQueryImpl::NewLC();
+
+                    cpQuery->iTitle->SetDataL( aData->Title().Data() );
+                    cpQuery->iTitle->SetKeyL( aData->Title().Key() );
+                    cpQuery->iBodyText->SetDataL( aData->BodyText().Data() );
+                    cpQuery->iBodyText->SetKeyL( aData->BodyText().Key() );
+                    cpQuery->iSemantics = MNcdQuery::ESemanticsRegistrationQuery;
+                    if ( iPurchaseQuery )
+                        {
+                        iPurchaseQuery->InternalRelease();
+                        iPurchaseQuery = NULL;
+                        }
+                    iPurchaseQuery  = CNcdQuery::NewL( *cpQuery );
+
+                    CleanupStack::PopAndDestroy( cpQuery );
+                    cpQuery = NULL;
+                    
+                    // Canceling resendAfter timer since there's no point
+                    // sending a request until the query has been handled
+                    DeletePtr( iDelay );
+                    CNcdBaseOperation::QueryReceivedL( iPurchaseQuery );
+
+                    DLTRACE(("created sms registration request ok"));    
+                    break;
+                    }
+                else 
+                    {
+                    DLTRACE(("incomplete"));    
+                    break;
+                    }
+                }
+            }
+        ); // TRAPD 
+        CleanupStack::PopAndDestroy( aData );
+        if( err != KErrNone )
+            {
+            iPurchaseOperationState = EFailed;
+            iError = err;
+            Cancel();
+            RunOperation();            
+            return;
+            }
+        }
+    else
+        {
+        CNcdBaseOperation::QueryL(aData);
+        }
+    DLTRACEOUT((""));
+    }
+
+
+void CNcdPurchaseOperationImpl::ProcessSmsPaymentL()
+    {
+    // Create a SMS for every SMS details received from the server.
+    for( TInt i = 0; i < iSelectedPaymentMethod->SmsDetailsCount(); i++ )
+	    {
+	    const MNcdPreminetProtocolSmsDetails* smsDetails = 
+	        &iSelectedPaymentMethod->SmsDetailsL( i );
+	    
+        // Store ongoing SMS operations to the array. They are removed in the 
+        // HandleSmsEvent callback.
+        iPendingSmsOperations.AppendL( 
+	    	iSmsSession->CreateSmsL( smsDetails->Address(),
+	    				             smsDetails->Message(),
+                                     this ) );
+	    }
+    }
+
+
+TInt CNcdPurchaseOperationImpl::HandleSmsEvent(
+	MCatalogsSmsOperation& aOperation, 
+	TCatalogsSmsEvent aEvent )
+	{
+    DLTRACEIN(("sms event: %d", aEvent));
+    
+    if ( iOperationState == EStateCancelled )
+        {
+        // If cancellation is originating from this operation let's
+        // not call runoperation with state EFailed as it calls the
+        // cancel again.
+        // Notice also that the runoperation is not called and therefore
+        // no observers are called in there. If necessary this has to
+        // be done in the cancel.
+        // No sms operation removal is done here. It is done in cancel.
+        return KErrNone;
+        }
+    
+	switch ( aEvent )
+	    {
+	    case ECatalogsSmsSent:
+	        {
+            // Remove completed SMS operation
+            RemovePendingSmsOp( aOperation.OperationId() );
+        	break;    
+	        }
+	    case ECatalogsSmsSending:
+	        {
+	        // Nothing to do
+	        break;
+	        }
+	        
+	    // Cancel here is a cancel that is not originating from 
+	    // this operation.
+	    // This is highly improbable, but the handling is here if anyone
+	    // would ever do such an implementation.	    
+	    case ECatalogsSmsCancelled: // Flow through
+	    case ECatalogsSmsSendingFailed:
+	        {
+            // Remove SMS operation
+        	RemovePendingSmsOp( aOperation.OperationId() );
+	        iPurchaseOperationState = EFailed;
+	        iError = KErrGsmSMSShortMessageTransferRejected;
+            break;
+	        }
+        default:
+            {
+            break;
+            }
+	    }
+	
+    // All SMS operations have been completed. Continue purchase process.
+    if( iPendingSmsOperations.Count() == 0 )
+	    {
+	    if ( iSmsRegistrationRequest ) 
+	        {	        
+	        // If the operation is already in failed state, let's not change
+	        // the state.
+	        if ( iPurchaseOperationState != EFailed )
+                {
+    	        iPurchaseOperationState = EBegin;
+                }
+    	    
+    	    delete iSmsRegistrationRequest;
+    	    iSmsRegistrationRequest = NULL;
+    	    
+    	    if ( iResendAfter != KResendAfterDisabled ) 
+    	        {
+    	        StartResendAfterTimer();
+    	        // we don't want to call RunOperation now
+    	        return KErrNone;
+    	        }
+	        }
+        else 
+            {
+            // If the operation is already in failed state, let's not change
+	        // the state.
+	        if ( iPurchaseOperationState != EFailed )
+                {
+    	        iPurchaseOperationState = EPaymentProcessed;
+                }
+            }
+        // continue operation
+        RunOperation();
+        }
+        
+        
+	DLTRACEOUT((""));
+	return KErrNone;
+	}
+
+
+
+void CNcdPurchaseOperationImpl::UpdatePurchaseHistoryL()
+    {
+    DLTRACEIN((""));
+    // Create purchase details and fill necessary information to it.
+    CNcdPurchaseDetails* purchaseDetails = CNcdPurchaseDetails::NewLC();
+    
+    // Fill purchase info
+
+    purchaseDetails->SetClientUid( iNode->NodeLinkL().MetaDataIdentifier().ClientUid() );
+    purchaseDetails->SetNamespaceL( iNode->NodeLinkL().MetaDataIdentifier().NodeNameSpace() );
+    purchaseDetails->SetEntityIdL( iNode->NodeLinkL().MetaDataIdentifier().NodeId() );
+    purchaseDetails->SetItemNameL( iNode->NodeMetaDataL().NodeName() );
+    
+    purchaseDetails->SetCatalogSourceNameL( iNode->NodeLinkL().CatalogsSourceName() );
+    purchaseDetails->SetPurchaseOptionIdL( iSelectedPurchaseOption->Id() );
+    purchaseDetails->SetServerUriL( iNode->NodeLinkL().ServerUri() );
+    purchaseDetails->SetItemType( MNcdPurchaseDetails::EItem ); // Should set this from node!
+    
+    TRAPD( err, SetContentInfoToPurchaseDetailsL( 
+        *purchaseDetails, iNode->NodeMetaDataL() ) );
+    
+    LeaveIfNotErrorL( err, KErrNotFound );
+         
+    
+    if( iNode->ClassId() == NcdNodeClassIds::ENcdSearchItemNodeClassId )
+        {
+        // this is a search node, get the origin id from parent node
+        CNcdSearchNodeFolder& searchFolder = iNodeManager->SearchFolderL(
+            iNode->NodeLinkL().ParentIdentifier() );
+        // NOTE: This sets some parent node's origin id as origin id.
+        // This is ok as the origin id is used only for retrieving the correct
+        // acccess point and ap:s are set only for catalogs/folders. Thus using 
+        // a parent catalog's/folder's origin id will result in correct results.
+        DLTRACE((_L("Search node, set origin node id from parent, resulting id=: %S"),
+            &searchFolder.OriginIdentifierL().NodeId() ));
+        purchaseDetails->SetOriginNodeIdL( searchFolder.OriginIdentifierL().NodeId() );
+        }
+    else
+        {
+        // this is a normal node, use it's id directly
+        DLTRACE((_L("Normal node use own id as origin node id=: %S"),
+            &iNode->Identifier().NodeId() ));
+        DLTRACE((_L("iNodeIdentifier=: %S"),
+            &iNodeIdentifier->NodeId() ));
+        purchaseDetails->SetOriginNodeIdL( iNode->Identifier().NodeId() );
+        }
+
+    DLINFO(("Node info set."));
+    
+    TRAP_IGNORE( HandleDependenciesL( *purchaseDetails ) );
+    
+    DLINFO(("Dependencies handled"));
+    
+            
+    CNcdPurchaseDownloadInfo* downloadInfo( NULL );
+        
+    TInt count( 0 );
+    if( !iSelectedPurchaseOption->IsFree() ||
+        iSelectedPurchaseOption->RequirePurchaseProcess() ||
+        iSelectedPurchaseOption->DownloadInfoCount() < 1 )
+        {
+        DLINFO(("Setting download details according to info received."));
+        
+        count = iPurchaseProcessedData->EntityL( 0 ).DownloadDetailsCount();
+        for( TInt i = 0; i < count; i++ )
+            {
+            downloadInfo = 
+                CNcdDownloadInfo::NewLC( 
+                    iPurchaseProcessedData->EntityL( 0 ).DownloadDetailsL( i ) );
+            purchaseDetails->AddDownloadInfoL( downloadInfo );           
+            CleanupStack::Pop( downloadInfo );
+            }
+            
+        // Only the price of one item is needed so it has to be taken from purchase option.
+        // Total price of iPurchaseInformationData is a price of a group of items if
+        // such a group is bought.
+        purchaseDetails->SetFinalPriceL( iSelectedPurchaseOption->PriceText() );
+        }
+    else
+        {
+        DLINFO(("Setting download details according to purchase option."));
+        
+        count = iSelectedPurchaseOption->DownloadInfoCount();
+        for( TInt i = 0; i < count; i++ )
+            {
+            downloadInfo = 
+                CNcdPurchaseDownloadInfo::NewLC( 
+                    iSelectedPurchaseOption->DownloadInfo( i ) );            
+            purchaseDetails->AddDownloadInfoL( downloadInfo );            
+            CleanupStack::Pop( downloadInfo );
+            }
+        purchaseDetails->SetFinalPriceL( iSelectedPurchaseOption->PriceText() );
+        }
+    DLINFO(("Download details set."));
+
+    // Add the correct number of empty file paths
+    for ( TInt i = 0; i < purchaseDetails->DownloadInfoCount(); ++i ) 
+        {
+        purchaseDetails->AddDownloadedFileL( KNullDesC );
+        }
+
+    purchaseDetails->SetDescriptionL( iNode->NodeMetaDataL().Description() );
+    DLINFO(("Description set."));
+    purchaseDetails->SetPurchaseOptionNameL( iSelectedPurchaseOption->PurchaseOptionName() );
+    purchaseDetails->SetPurchaseOptionPriceL( iSelectedPurchaseOption->PriceText() );
+    DLINFO(("Purchase option info set."));
+    
+    // Set the purchase time. Note that this is universal time, not local time.
+    // This helps in syncing the purchase history with pc client front end.
+    // UI must convert this to local time before showing it.
+    TTime time;
+    time.UniversalTime();
+    purchaseDetails->SetPurchaseTime( time );
+    DLINFO(("Purchase time set."));
+    
+   
+    // If icon can not be found, set null. Done because at the moment
+    // we don't have icons for all items.
+    HBufC8* iconData( NULL );
+    TRAP_IGNORE( iconData = iNode->NodeMetaDataL().IconL().IconDataL() );
+    
+    if ( iconData ) 
+        {        
+        purchaseDetails->SetIcon( iconData );
+    
+        DLINFO(("Icon set."));
+        }
+    
+    purchaseDetails->SetLastUniversalOperationTime();
+    purchaseDetails->SetLastOperationErrorCode( KErrNone );    
+    
+    // Save it to the purchase history
+    iPurchaseHistory->SavePurchaseL( *purchaseDetails );
+    
+    CleanupStack::PopAndDestroy( purchaseDetails );
+    DLTRACEOUT((""));
+    }
+
+
+void CNcdPurchaseOperationImpl::SetContentInfoToPurchaseDetailsL( 
+    CNcdPurchaseDetails& aDetails, 
+    const CNcdNodeMetaData& aMetadata ) const
+    {
+    DLTRACEIN((""));
+    const CNcdNodeContentInfo& info( aMetadata.ContentInfoL() );
+        
+    aDetails.SetTotalContentSize( info.TotalContentSize() );
+    aDetails.SetVersionL( info.Version() );
+    aDetails.SetItemPurpose( info.Purpose() );  
+    aDetails.SetAttributeL( 
+        MNcdPurchaseDetails::EPurchaseAttributeContentUid, 
+        info.Uid().iUid );
+
+    aDetails.SetAttributeL( 
+        MNcdPurchaseDetails::EPurchaseAttributeContentMimeType, 
+        info.MimeType() );
+    
+    }
+
+
+void CNcdPurchaseOperationImpl::HandleDependenciesL( 
+    CNcdPurchaseDetails& aDetails )
+    {
+    DLTRACEIN((""));
+    CNcdNodeMetaData& metadata( iNode->NodeMetaDataL() );
+    
+    const CNcdNodeDependency& dep( metadata.DependencyL() );
+    AddDownloadInfosToDetailsL( aDetails, dep.ContentTargets() );
+    DLTRACEOUT(("Dependencies handled successfully"));
+    }
+
+
+void CNcdPurchaseOperationImpl::AddDownloadInfosToDetailsL( 
+    CNcdPurchaseDetails& aDetails, 
+    const RPointerArray<CNcdDownloadInfo>& aInfos )
+    {
+    DLTRACEIN((""));
+    CNcdPurchaseDownloadInfo* downloadInfo( NULL );
+    CNcdPurchaseInstallInfo* installInfo( NULL );
+    for( TInt i = 0; i < aInfos.Count(); i++ )
+        {
+        downloadInfo = 
+            CNcdPurchaseDownloadInfo::NewLC( 
+                *aInfos[ i ] );
+        aDetails.AddDownloadInfoL( downloadInfo );           
+        CleanupStack::Pop( downloadInfo );        
+        
+        DLTRACE(( _L("Adding install info, uid: %x, version: %S"), 
+            aInfos[i]->ContentId(), &aInfos[i]->ContentVersion() ));
+            
+        installInfo = CNcdPurchaseInstallInfo::NewLC();
+        installInfo->SetApplicationUid( aInfos[i]->ContentId() );
+        installInfo->SetApplicationVersionL( aInfos[i]->ContentVersion() );
+        aDetails.AddInstallInfoL( installInfo );
+        CleanupStack::Pop( installInfo );
+        }
+    DLTRACEOUT(("Download infos added successfully"));
+    }
+
+
+TInt CNcdPurchaseOperationImpl::RemovePendingSmsOp(
+    const TCatalogsTransportOperationId& aId )
+    {
+	for( TInt i = 0; i < iPendingSmsOperations.Count(); i++ )
+	    {
+	    if( iPendingSmsOperations[i]->OperationId() == aId )
+	        {
+    	    iPendingSmsOperations[i]->Release();
+	        iPendingSmsOperations.Remove( i );
+	        return KErrNone;
+	        }
+	    }
+	return KErrNotFound;
+    }
+
+TBool CNcdPurchaseOperationImpl::QueryCompletedL( CNcdQuery* aQuery )
+    {
+    DLTRACEIN((""));
+    DLINFO(("query response: %d", aQuery->Response() ));
+    TBool handled = EFalse;
+    aQuery->InternalAddRef();
+    CleanupInternalReleasePushL( *aQuery );
+    
+    if( iPurchaseQuery && iPurchaseQuery == aQuery )
+        {
+        // this is a purchase query (i.e. initiated directly from purchase op)
+        if( iPurchaseOperationState == EPurchaseComplete )
+            {
+            // Operation is already complete.
+            // This is needed for purchase information, which is sent last 
+            // just before completing the operation.
+            iPurchaseQuery->InternalRelease();
+            iPurchaseQuery = NULL;
+            }
+        else if ( aQuery->Semantics() == MNcdQuery::ESemanticsRegistrationQuery &&
+                    iSmsRegistrationRequest ) 
+            {
+            if ( aQuery->Response() == MNcdQuery::EAccepted ) 
+                {
+                iPendingSmsOperations.AppendL( 
+        	    	iSmsSession->CreateSmsL( iSmsRegistrationRequest->Key(),
+        	    				             iSmsRegistrationRequest->Value(),
+                                             this ) );
+                }
+            else 
+                {
+                delete iSmsRegistrationRequest;
+                iSmsRegistrationRequest = 0;
+                }
+            // query is now handled, release it
+            iPurchaseQuery->InternalRelease();
+            iPurchaseQuery = NULL;
+            }
+        // in the rest of the cases the query must not be rejected or op will fail
+        else if ( aQuery->Response() == MNcdQuery::ERejected )
+            {
+            DLTRACE(("Query rejected -> fail operation"));
+            iError = KNcdErrorMandatoryQueryRejected;
+            iPurchaseOperationState = EFailed;
+            // query is now handled, release it
+            iPurchaseQuery->InternalRelease();
+            iPurchaseQuery = NULL;
+            User::Leave( KNcdErrorMandatoryQueryRejected );
+            }
+        else if( aQuery->Id() == KQueryIdPaymentMethod )
+            {
+            CNcdQuerySelectionItem* item = static_cast<CNcdQuerySelectionItem*>( &aQuery->QueryItemL( 0 ) );
+            iSelectedPaymentMethod = &iPurchaseInformationData->PaymentL( item->Selection() );
+            // query is now handled, release it
+            iPurchaseQuery->InternalRelease();
+            iPurchaseQuery = NULL;
+            }
+        else if ( aQuery->Semantics() == MNcdQuery::ESemanticsDisclaimer )
+            {
+            // disclaimer does not require a response
+            // query is now handled, release it
+            iPurchaseQuery->InternalRelease();
+            iPurchaseQuery = NULL;
+            }
+        else
+            {
+            // payment info query, don't release, response needs to be added to
+            // confirmation request
+            }
+        handled = ETrue;
+        }
+    CleanupStack::PopAndDestroy( aQuery );
+    return handled;
+    }
+
+TInt CNcdPurchaseOperationImpl::ResendAfterCallBack( TAny* aOperation ) 
+    {
+    DLTRACEIN((""));
+    CNcdPurchaseOperationImpl* purchaseOp = static_cast<CNcdPurchaseOperationImpl*>( aOperation );
+    purchaseOp->iLocked = EFalse;
+    purchaseOp->iResendAfter = KResendAfterDisabled;
+    return purchaseOp->RunOperation();
+    }
+
+
+TInt CNcdPurchaseOperationImpl::StartResendAfterTimer()
+    {
+    DLTRACEIN((""));
+    TCallBack cb( ResendAfterCallBack, this );
+    delete iDelay;
+    iDelay = NULL;
+    
+    TRAPD( err, iDelay = CNcdDelay::NewL( cb ) );
+    if ( err == KErrNone ) 
+        {
+        iDelay->After( iResendAfter * 1000000 );
+        iLocked = ETrue;
+        }
+    else
+        {
+        iPurchaseOperationState = EFailed;
+        iError = err;
+        Cancel();
+        RunOperation();
+        }
+    
+    return err;
+    }