diff -r 000000000000 -r ba25891c3a9e ncdengine/provider/client/src/ncdnodepurchaseproxy.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ncdengine/provider/client/src/ncdnodepurchaseproxy.cpp Thu Dec 17 08:51:10 2009 +0200 @@ -0,0 +1,792 @@ +/* +* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +#include + +#include "ncdnodepurchaseproxy.h" + +#include "ncdpurchaseoptionproxy.h" +#include "ncdpurchaseoperationproxy.h" +#include "ncdsubscriptionmanagerproxy.h" +#include "ncdclientpartofsubscription.h" +#include "ncdclientsubscribablecontent.h" +#include "ncdnodemanagerproxy.h" + +#include "ncdnodeproxy.h" +#include "ncdnodemetadataproxy.h" +#include "ncdoperationmanagerproxy.h" +#include "ncdoperationimpl.h" +#include "catalogsclientserver.h" +#include "ncdnodeidentifier.h" +#include "ncdnodefunctionids.h" +#include "ncdnodeclassids.h" +#include "catalogsinterfaceidentifier.h" +#include "catalogsutils.h" +#include "catalogsdebug.h" +#include "catalogsconstants.h" +#include "ncderrors.h" + + +// ======== MEMBER FUNCTIONS ======== + + +CNcdNodePurchaseProxy::CNcdNodePurchaseProxy( + MCatalogsClientServer& aSession, + TInt aHandle, + CNcdNodeMetadataProxy& aMetadata ) + : CNcdInterfaceBaseProxy( aSession, aHandle, &aMetadata ), + iMetadata( aMetadata ) + { + // This would be faster in the construct list but because + // resetting is needed also elsewhere it is better to keep + // the reset values only in one place. + ResetHistoryData(); + } + +void CNcdNodePurchaseProxy::ConstructL() + { + DLTRACEIN(("")); + + // Register the interface + MNcdNodePurchase* purchase( this ); + AddInterfaceL( CCatalogsInterfaceIdentifier::NewL( + purchase, + this, + MNcdNodePurchase::KInterfaceUid ) ); + // Try to internalize already here. And if it fails, + // then let it leave. + InternalizeL(); + + DLTRACEOUT(("Success. Options: %d", iPurchaseOptions.Count())); + } + + +CNcdNodePurchaseProxy* CNcdNodePurchaseProxy::NewL( + MCatalogsClientServer& aSession, + TInt aHandle, + CNcdNodeMetadataProxy& aMetadata ) + { + CNcdNodePurchaseProxy* self = + CNcdNodePurchaseProxy::NewLC( aSession, + aHandle, + aMetadata ); + CleanupStack::Pop( self ); + return self; + } + + +CNcdNodePurchaseProxy* CNcdNodePurchaseProxy::NewLC( + MCatalogsClientServer& aSession, + TInt aHandle, + CNcdNodeMetadataProxy& aMetadata ) + { + CNcdNodePurchaseProxy* self = + new( ELeave ) CNcdNodePurchaseProxy( aSession, + aHandle, + aMetadata ); + // Using PushL because the object does not have any references yet + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + + +CNcdNodePurchaseProxy::~CNcdNodePurchaseProxy() + { + DLTRACEIN(("")); + // Remove interfaces implemented by this class from the interface list. + // So, the interface list is up to date when this class object is deleted. + RemoveInterface( MNcdNodePurchase::KInterfaceUid ); + + delete iSubscribableContent; + iSubscribableContent = NULL; + + // When purchase is destroyed, also purchaseoptions go + // with it if no references are left to them. + DeletePurchaseOptions(); + + ResetHistoryData(); + + DLTRACEOUT(("")); + } + +void CNcdNodePurchaseProxy::InternalizeL() + { + DLTRACEIN(("")); + + TRAPD( error, + { + InternalizeMeansL(); + InternalizeHistoryL(); + } ); + if ( error != KErrNone ) + { + DLERROR(("Purchase internalize error")); + // Because we can't fully construct the purchase info, + // we don't give even parts of info to the user because it could + // be misleading. + DeletePurchaseOptions(); + ResetHistoryData(); + User::Leave( error ); + } + DLTRACEOUT(("")); + } + + +void CNcdNodePurchaseProxy::InternalizeMeansL() + { + DLTRACEIN(("")); + + // Request the purchase option ids from server side + CDesCArray* poIds = RequestPurchaseOptionIdsL(); + CleanupStack::PushL( poIds ); + + // Delete purchase options that don't exist in server side. + ReleaseMissingPurchaseOptions( *poIds ); + + // Reinternalize purchase options that existed already. + InternalizeExistingPurchaseOptionsL(); + + // Create purchase options that exists in the server side but not + // in proxy side. + CDesCArray* newPurchaseOptions = new( ELeave ) CDesCArrayFlat( 5 ); + CleanupStack::PushL( newPurchaseOptions ); + + // Find the purchase options that don't exist in the proxy side. + for ( TInt i = 0; i < poIds->Count(); i++ ) + { + TPtrC id = (*poIds)[i]; + if ( !HasPurchaseOption( id ) ) + { + newPurchaseOptions->AppendL( id ); + } + } + + InternalizeMeansL( *newPurchaseOptions ); + CleanupStack::PopAndDestroy( newPurchaseOptions ); + newPurchaseOptions = NULL; + CleanupStack::PopAndDestroy( poIds ); + poIds = NULL; + + DLTRACEOUT(("")); + } + + +void CNcdNodePurchaseProxy::InternalizeHistoryL() + { + DLTRACEIN(("")); + + HBufC8* data( NULL ); + + // Because we do not know the exact size of the data id, use + // the alloc method, which creates the buffer of the right size + // and sets the pointer to point to the created buffer. + User::LeaveIfError( + ClientServerSession(). + SendSyncAlloc( NcdNodeFunctionIds::ENcdInternalizePurchaseHistory, + KNullDesC8, + data, + Metadata().Handle(), + 0 ) ); + + if ( data == NULL ) + { + User::Leave( KErrNotFound ); + } + + CleanupStack::PushL( data ); + + + // Read the data from the stream + RDesReadStream stream( *data ); + CleanupClosePushL( stream ); + + TRAPD( error, InternalizeHistoryDataL( stream ) ); + if ( error != KErrNone ) + { + User::Leave( error ); + } + + // Closes the stream + CleanupStack::PopAndDestroy( &stream ); + CleanupStack::PopAndDestroy( data ); + + DLTRACEOUT(("")); + } + +CNcdNodeMetadataProxy& CNcdNodePurchaseProxy::Metadata() const + { + return iMetadata; + } + +const CNcdClientSubscribableContent* + CNcdNodePurchaseProxy::SubscribableContent() const + { + DLTRACEIN(("")); + DLTRACEOUT(("")); + return iSubscribableContent; + } + + + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// In this function we return purchaseoptions to ui that are available for +// the client at the moment. +// --------------------------------------------------------------------------- +// +RCatalogsArray + CNcdNodePurchaseProxy::PurchaseOptionsL() const + { + DLTRACEIN(( "" )); + + RCatalogsArray options; + + // in case of leave, destroy its contents (release) + CleanupResetAndDestroyPushL( options ); + + + TInt optionsCount( iPurchaseOptions.Count() ); + TInt optionsIndexer( 0 ); + while ( optionsIndexer < optionsCount ) + { + DASSERT( iPurchaseOptions[optionsIndexer] ); + + // Purchaseoption that we handle at the moment + CNcdPurchaseOptionProxy* purchaseOption = + iPurchaseOptions[optionsIndexer]; + + // Type of the purchaseoption + MNcdPurchaseOption::TType purchaseoptionType = + purchaseOption->PurchaseOptionType(); + + // dataentity to whome the purchaseoption belongs to + CNcdNodeMetadataProxy& meta = + purchaseOption->ParentNodePurchase()->Metadata(); + + + + // NOTICE: At the moment this function does not take into count + // situations where purchaseoption could be of several + // types like ESubscription and ESubscriptionPurchase + + + // Can purchase of the item be done if it is already + // purchased? + + + if ( purchaseoptionType == MNcdPurchaseOption::EPurchase ) + { + + // Should check if parentfolder has separately purchaseable + // false and if it has then do not show normal purchase + // purchaseoptions to ui + + options.AppendL( purchaseOption ); + TInt ref = purchaseOption->AddRef(); + DLTRACE(("Purchase option ref count after addref: %d", ref)); + } + else if ( purchaseoptionType == + MNcdPurchaseOption::ESubscriptionPurchase ) + { + // Part of subscription -type purchaseoptions are + // always shown to ui + options.AppendL( purchaseOption ); + purchaseOption->AddRef(); + } + else if ( purchaseoptionType == + MNcdPurchaseOption::ESubscription ) + { + // If this subscription is already bought, we can't show + // it to ui. + + TBool isBought = + Metadata().Node().NodeManager(). + SubscriptionManager(). + ActiveSubscriptionExists( meta.Id(), + meta.Namespace(), + purchaseOption->Id() ); + + if ( !isBought ) + { + options.AppendL( purchaseOption ); + purchaseOption->AddRef(); + } + } + else if ( purchaseoptionType == + MNcdPurchaseOption::ESubscriptionUpgrade ) + { + // If target of the purchaseoption is not bought, upgrade + // cannot be used + if ( purchaseOption->IsUsableL() ) + { + options.AppendL( purchaseOption ); + purchaseOption->AddRef(); + } + } + else + { + // Not supported + DASSERT( false ); + } + + ++optionsIndexer; + } + + + CleanupStack::Pop( &options ); + + DLTRACEOUT(( "" )); + return options; + } + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// ?implementation_description +// --------------------------------------------------------------------------- +// +MNcdPurchaseOption* CNcdNodePurchaseProxy::PurchaseOptionL( + const TDesC& aPurchaseOptionId ) const + { + DLTRACEIN(("")); + TInt optionsCount( iPurchaseOptions.Count() ); + TInt optionsIndexer( 0 ); + while ( optionsIndexer < optionsCount ) + { + DASSERT( iPurchaseOptions[optionsIndexer] ); + + if ( iPurchaseOptions[optionsIndexer]->Id() == aPurchaseOptionId ) + { + iPurchaseOptions[optionsIndexer]->AddRef(); + + DLTRACEOUT(( "" )); + return iPurchaseOptions[optionsIndexer]; + } + + ++optionsIndexer; + } + + DLTRACEOUT(( "" )); + return NULL; + } + + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// Notice that this function accepts purchase options also from other nodes +// than into which this interface belongs to. Could be changed to accept +// only purchase options from this interface, but at the moment UI +// does not permit this in case of subscription. +// --------------------------------------------------------------------------- +// +MNcdPurchaseOperation* CNcdNodePurchaseProxy::PurchaseL( + MNcdPurchaseOption& aPurchaseOption, + MNcdPurchaseOperationObserver& aObserver ) + { + DLTRACEIN(("")); + + + CNcdPurchaseOperationProxy* operation( NULL ); + + CNcdPurchaseOptionProxy& option = + *static_cast( &aPurchaseOption ); + + + // Given purchaseoption is old and should not be used anymore + if ( option.IsObsolete() ) + { + User::Leave( KNcdErrorObsolete ); + } + + + // Now if validuntil for subscription has passed, we should not allow it + // to be bought anymore + if ( option.PurchaseOptionType() == MNcdPurchaseOption::ESubscription ) + { + TTime now; + now.HomeTime(); + + // The time until the node is subscribable is stored in the + // subscribable content of the NodePurchase interface where to + // the GIVEN PURCHASEOPTION belongs to. + const CNcdClientSubscribableContent* subscribableContent = + option.ParentNodePurchase()->SubscribableContent(); + + DASSERT( subscribableContent ); + if ( subscribableContent && + subscribableContent->ValidUntilSet() && + now > subscribableContent->ValidUntil() ) + { + User::Leave( KNcdErrorSubscriptionNotSubscribableAnymore ); + } + + } + + + DLTRACE(( _L("Purchase started with option whose name is: %S"), + &option.Name() )); + + + // NOTICE: In the case of subscription or upgrade the purchaseoption + // can be from another node. That is why we give + // node-reference from the purchaseoption. + operation = + Metadata().Node().OperationManager(). + CreatePurchaseOperationL( option.ParentNodePurchase()->Metadata().Node(), + option, + &aObserver ); + + // It is enough that the observer is informed + // when the file operation is ready. So, no need to add + // node or this class object to be observers. + + // No need to increase the operation ref count here because + // it should be initially one. + + DLTRACEOUT(("")); + + return operation; + } + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// ?implementation_description +// --------------------------------------------------------------------------- +// +TBool CNcdNodePurchaseProxy::IsPurchased() const + { + return iIsPurchased; + } + + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// ?implementation_description +// --------------------------------------------------------------------------- +// +MNcdPurchaseOption* CNcdNodePurchaseProxy::PurchasedOptionL() const + { + if ( !IsPurchased() ) + { + return NULL; + } + + return PurchaseOptionL( *iPurchasedOptionId ); + } + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// ?implementation_description +// --------------------------------------------------------------------------- +// +TTime CNcdNodePurchaseProxy::TimeOfPurchaseL() const + { + if ( !IsPurchased() ) + { + User::Leave( KErrNotFound ); + } + + return iTimeOfPurchase; + } + +// --------------------------------------------------------------------------- +// From class MNcdNodePurchase. +// ?implementation_description +// --------------------------------------------------------------------------- +// +const TDesC& CNcdNodePurchaseProxy::PurchasedPriceL() const + { + if ( !IsPurchased() ) + { + User::Leave( KErrNotFound ); + } + + return *iPurchasedPrice; + } + + +void CNcdNodePurchaseProxy::InternalizeMeansDataL( RReadStream& aStream ) + { + DLTRACEIN(("")); + + // Subscribable content is read into its own class + TBool subscribableContentFound( aStream.ReadInt32L() ); + if ( subscribableContentFound ) + { + if ( iSubscribableContent == NULL ) + { + iSubscribableContent = CNcdClientSubscribableContent::NewL(); + } + iSubscribableContent->InternalizeL( aStream ); + } + + + TInt handleCount = aStream.ReadInt32L(); + DLINFO((( "handle count: %d" ), handleCount )); + + for ( TInt i = 0; i < handleCount; i++ ) + { + TInt tmpProxyHandle = aStream.ReadInt32L(); + CNcdPurchaseOptionProxy* tmpPurchaseOption = + CNcdPurchaseOptionProxy::NewLC( + ClientServerSession(), + tmpProxyHandle, + *this ); + + TRAPD( addError, iPurchaseOptions.AppendL( tmpPurchaseOption ) ); + if ( addError != KErrNone ) + { + CleanupStack::PopAndDestroy( tmpPurchaseOption ); + User::Leave( addError ); + } + + CleanupStack::Pop( tmpPurchaseOption ); + tmpPurchaseOption->InternalizeL(); + } + + DLTRACEOUT(("")); + } + + +void CNcdNodePurchaseProxy::InternalizeHistoryDataL( RReadStream& aStream ) + { + DLTRACEIN(("")); + + // Reset values so that if we receive message that for some reason + // does not contain purchase history info, we don't keep the old + // purchase history info in memory. + ResetHistoryData(); + + // Node class id. Not actually needed. + aStream.ReadInt32L(); + + + iIsPurchased = aStream.ReadInt32L(); + if ( iIsPurchased == EFalse ) + { + return; + } + + InternalizeDesL( iPurchasedOptionId, aStream ); + + TInt64 integerTimeOfPurchase( 0 ); + // Store framework provides the necessary implementation for + // the operator>> to internalise the 64-bit integer + aStream >> integerTimeOfPurchase; + iTimeOfPurchase = TTime( integerTimeOfPurchase ); + + InternalizeDesL( iPurchasedPrice, aStream ); + + + DLTRACEOUT(("")); + } + + +void CNcdNodePurchaseProxy::DeletePurchaseOptions() + { + DLTRACEIN(("Handling %d purchase options", iPurchaseOptions.Count() )); + for ( TInt i = iPurchaseOptions.Count() - 1; i >= 0; --i ) + { + CNcdPurchaseOptionProxy* tmpOption = iPurchaseOptions[i]; + + if ( tmpOption->MyTotalRefCount() > 0 ) + { + // to prevent loops remove purchaseoption first + // so it won't become deleted + iPurchaseOptions.Remove( i ); + + tmpOption->SetObsolete(); + // Remove the purchase option from the interface parent. + // This will update the parent reference count. + // Because purchase option does not provide any interfaces for + // the parent, it is safe to use this function. + tmpOption->RemoveFromParent(); + if ( tmpOption->TotalRefCount() == 0 ) + { + DLINFO(("Purchase option can be deleted here.")); + delete tmpOption; + } + } + } + DLTRACE(("Deleting %d purchase options", iPurchaseOptions.Count() )); + iPurchaseOptions.ResetAndDestroy(); + } + +void CNcdNodePurchaseProxy::ResetHistoryData() + { + iIsPurchased = EFalse; + delete iPurchasedOptionId; + iPurchasedOptionId = NULL; + iTimeOfPurchase = TTime( 0 ); + delete iPurchasedPrice; + iPurchasedPrice = NULL; + } + +CDesCArray* CNcdNodePurchaseProxy::RequestPurchaseOptionIdsL() + { + DLTRACEIN(("")); + + HBufC8* data( NULL ); + + // Because we do not know the exact size of the data, use + // the alloc method, which creates the buffer of the right size + // and sets the pointer to point to the created buffer. + User::LeaveIfError( + ClientServerSession(). + SendSyncAlloc( NcdNodeFunctionIds::ENcdPurchaseOptionIds, + KNullDesC8, + data, + Handle(), + 0 ) ); + + if ( data == NULL ) + { + User::Leave( KErrNotFound ); + } + + CleanupStack::PushL( data ); + + // Read the data from the stream + RDesReadStream stream( *data ); + CleanupClosePushL( stream ); + + TInt purchaseOptionCount = stream.ReadInt32L(); + CDesCArray* array = new( ELeave ) CDesCArrayFlat( 5 ); + CleanupStack::PushL( array ); + for ( TInt i = 0; i < purchaseOptionCount; i++ ) + { + HBufC* des( NULL ); + InternalizeDesL( des, stream ); + CleanupStack::PushL( des ); + array->AppendL( *des ); + CleanupStack::PopAndDestroy( des ); + } + + CleanupStack::Pop( array ); + CleanupStack::PopAndDestroy( &stream ); + CleanupStack::PopAndDestroy( data ); + + return array; + } + +void CNcdNodePurchaseProxy::ReleaseMissingPurchaseOptions( + const CDesCArray& aPurchaseOptionIds ) + { + DLTRACEIN(("")); + + for ( TInt i = iPurchaseOptions.Count() - 1; i >= 0; i-- ) + { + TInt index; + if ( aPurchaseOptionIds.Find( iPurchaseOptions[i]->Id(), index ) != 0 ) + { + iPurchaseOptions[i]->SetObsolete(); + // Remove the purchase option from the interface parent. + // This will update the parent reference count. + // Because purchase option does not provide any interfaces for + // the parent, it is safe to use this function. + iPurchaseOptions[i]->RemoveFromParent(); + if ( iPurchaseOptions[ i ]->TotalRefCount() == 0 ) + { + DLINFO(("Purchase option can be deleted.")); + delete iPurchaseOptions[ i ]; + } + iPurchaseOptions.Remove( i ); + } + } + } + +void CNcdNodePurchaseProxy::InternalizeExistingPurchaseOptionsL() + { + DLTRACEIN(("")); + + for ( TInt i = 0; i < iPurchaseOptions.Count(); i++ ) + { + iPurchaseOptions[i]->InternalizeL(); + } + } + +void CNcdNodePurchaseProxy::InternalizeMeansL( + const CDesCArray& aPurchaseOptionIds ) + { + DLTRACEIN(("")); + + CBufBase* buf = CBufFlat::NewL( KBufExpandSize ); + CleanupStack::PushL( buf ); + + RBufWriteStream writeStream( *buf ); + CleanupClosePushL( writeStream ); + writeStream.WriteInt32L( aPurchaseOptionIds.Count() ); + for ( TInt i = 0; i < aPurchaseOptionIds.Count(); i++ ) + { + ExternalizeDesL( aPurchaseOptionIds[i], writeStream ); + } + + CleanupStack::PopAndDestroy( &writeStream ); + HBufC8* messageToSend = HBufC8::NewL( buf->Size() ); + messageToSend->Des().Copy( buf->Ptr( 0 ) ); + + CleanupStack::PopAndDestroy( buf ); + CleanupStack::PushL( messageToSend ); + + // Request handles to the new purchase options. + HBufC8* data( NULL ); + + // Because we do not know the exact size of the data, use + // the alloc method, which creates the buffer of the right size + // and sets the pointer to point to the created buffer. + User::LeaveIfError( + ClientServerSession(). + SendSyncAlloc( NcdNodeFunctionIds::ENcdInternalizePurchaseMeans, + *messageToSend, + data, + Handle(), + 0 ) ); + + CleanupStack::PopAndDestroy( messageToSend ); + messageToSend = NULL; + + if ( data == NULL ) + { + User::Leave( KErrNotFound ); + } + + CleanupStack::PushL( data ); + + // Read the data from the stream + RDesReadStream stream( *data ); + CleanupClosePushL( stream ); + + InternalizeMeansDataL( stream ); + + CleanupStack::PopAndDestroy( &stream ); + CleanupStack::PopAndDestroy( data ); + } + +TBool CNcdNodePurchaseProxy::HasPurchaseOption( const TDesC& aId ) const + { + DLTRACEIN(("")); + for ( TInt i = 0; i < iPurchaseOptions.Count(); i++ ) + { + if ( iPurchaseOptions[i]->Id() == aId ) + { + return ETrue; + } + } + + return EFalse; + }