diff -r 000000000000 -r ba25891c3a9e ncdengine/provider/server/src/ncdpurchaseoperationimpl.cpp --- /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 +#include +#include // 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 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 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& 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& 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( &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( 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; + }