--- /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;
+ }