diff -r 000000000000 -r 32704c33136d ncdengine/provider/purchasehistory/src/ncdpurchasehistorydbimpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ncdengine/provider/purchasehistory/src/ncdpurchasehistorydbimpl.cpp Tue Jan 26 12:06:03 2010 +0200 @@ -0,0 +1,1263 @@ +/* +* 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 +#include +#include + +#include "ncdpurchasehistorydbimpl.h" +#include "ncdpurchasedownloadinfo.h" +#include "ncdpurchaseinstallinfo.h" +#include "ncdpurchasehistoryfilter.h" +#include "ncdnodefunctionids.h" +#include "catalogssession.h" +#include "catalogsbasemessage.h" +#include "catalogsbigdes.h" +#include "catalogsconstants.h" +#include "catalogsutils.h" +#include "ncdutils.h" + +#include "catalogsdebug.h" + +// Create a purchases table. +// This column order is always used when inserting data or in queries. +// Column numbering is stored in CNcdPurchaseHistoryDb::PurchaseColumns. +_LIT( KSqlCreatePurchasesTable, "\ + CREATE TABLE purchases ( \ + purchase_id COUNTER,\ + event_id UNSIGNED INTEGER,\ + client_uid INTEGER,\ + namespace LONG VARCHAR,\ + entity_id LONG VARCHAR,\ + item_name LONG VARCHAR,\ + item_purpose UNSIGNED INTEGER,\ + catalog_name LONG VARCHAR,\ + download_info LONG VARBINARY,\ + purchase_option_id LONG VARCHAR,\ + purchase_option_name LONG VARCHAR,\ + purchase_option_price LONG VARCHAR,\ + final_price LONG VARCHAR,\ + payment_method_name LONG VARCHAR,\ + purchase_time BIGINT,\ + downloaded_files LONG VARBINARY,\ + file_install_infos LONG VARBINARY,\ + icon LONG VARBINARY,\ + downloadaccesspoint LONG VARCHAR,\ + description LONG VARCHAR,\ + version LONG VARCHAR,\ + server_uri LONG VARCHAR,\ + item_type INTEGER, \ + total_content_size INTEGER, \ + origin_node_id LONG VARCHAR, \ + last_operation_time BIGINT, \ + last_operation_error_code INTEGER, \ + has_icon INTEGER, \ + attributes LONG VARBINARY )" ); + + +// Create a event counter table. +_LIT( KSqlCreateEventCounterTable, + "CREATE TABLE event_counter_table ( event_counter UNSIGNED INTEGER )" ); + +// Event counter column number. +const TInt KEventCounterColumnNumber = 1; + + +// Select purchases. +_LIT( KSqlPurchasesStart, + "SELECT purchase_id FROM purchases WHERE event_id >= " ); +_LIT( KSqlPurchasesNamespace, " AND namespace = '" ); +_LIT( KSqlPurchasesEntityId, " AND entity_id = '" ); +_LIT( KSqlPurchasesClientUid, " AND ( client_uid = " ); +_LIT( KSqlPurchasesOrClientUid, " OR client_uid = " ); +_LIT( KSqlPurchasesEndClientUid, " )" ); +_LIT( KSqlPurchasesEnd, "'" ); +_LIT( KSqlPurchasesOrderNewestFirst, " ORDER BY event_id DESC" ); +_LIT( KSqlPurchasesOrderOldestFirst, " ORDER BY event_id ASC" ); + +// Select purchase to be updated. +_LIT( KSqlPurchaseUpdateStart, + "SELECT * FROM purchases WHERE client_uid = " ); +_LIT( KSqlPurchasesUpdateNamespace, " AND namespace = '" ); +_LIT( KSqlPurchasesUpdateEntityId, "' AND entity_id = '" ); +_LIT( KSqlPurchasesUpdatePurchaseTime, "' AND purchase_time = " ); +_LIT( KSqlPurchasesUpdateEnd, "" ); + +// Select purchase. +_LIT( KSqlPurchaseByPurchaseIdStart, + "SELECT * FROM purchases WHERE purchase_id = " ); +_LIT( KSqlPurchaseByPurchaseIdEnd, "" ); + +// Select all purchases. +_LIT( KSqlPurchasesAllNone, "SELECT * FROM purchases" ); + +// Delete specified purchase event. +_LIT( KSqlPurchasesDeleteStart, + "DELETE FROM purchases WHERE purchase_id = " ); +_LIT( KSqlPurchasesDeleteEnd, "" ); + + +// Select current event count. +_LIT( KSqlCurrentEventCount, + "SELECT event_counter FROM event_counter_table" ); + +// Delete event count from event count table. +_LIT( KSqlEventCountDelete, "DELETE FROM event_counter_table" ); + +// "-2147483648" +const TInt KMaxLengthOfInt = 11; + +// "-9223372036854775808" +const TInt KMaxLengthOfTint64 = 20; + +static void AppendWithQuotesDuplicatedL( + CCatalogsBigDes* aOutput, + const TDesC& aInput ) + { + _LIT( KQuote, "'" ); + + if ( aOutput == NULL || &aInput == NULL ) + { + return; + } + + TPtrC in = aInput.Mid( 0 ); + while ( in.Length() > 0 ) + { + TInt i = in.Locate( '\'' ); + if ( i == KErrNotFound ) + { + // Quote not found, all done + aOutput->AppendL( in ); + return; + } + aOutput->AppendL( in.Mid( 0, i + 1 ) ); + // Append extra quote + aOutput->AppendL( KQuote ); + if ( i + 1 < in.Length() ) + { + // Get end part of the input descriptor + in.Set( in.Mid( i + 1 ) ); + } + else + { + // End of the input descriptor reached + return; + } + + } + } + +static HBufC* ReadLongTextColumnL( RDbRowSet aView, TDbColNo aCol ) + { + TInt length = aView.ColLength( aCol ); + if ( length == 0 ) + { + return KNullDesC().Alloc(); + } + RDbColReadStream readStream; + readStream.OpenLC( aView, aCol ); + HBufC* result = HBufC::NewLC( length ); + TPtr resultPtr = result->Des(); + readStream.ReadL( resultPtr, length ); + readStream.Close(); + CleanupStack::Pop( result ); + CleanupStack::Pop(); //readStream + return result; + } + +static void WriteLongTextColumnL( + RDbRowSet aView, + TDbColNo aCol, + const TDesC& aValue ) + { + RDbColWriteStream writeStream; + writeStream.OpenLC( aView, aCol ); + if ( &aValue ) + { + writeStream.WriteL( aValue ); + } + else + { + writeStream.WriteL( KNullDesC ); + } + writeStream.Close(); + CleanupStack::Pop( &writeStream ); + } + +static HBufC8* ReadLongTextColumn8L( RDbRowSet aView, TDbColNo aCol ) + { + TInt length = aView.ColLength( aCol ); + if ( length == 0 ) + { + return KNullDesC8().Alloc(); + } + RDbColReadStream readStream; + readStream.OpenLC( aView, aCol ); + HBufC8* result = HBufC8::NewLC( length ); + TPtr8 resultPtr = result->Des(); + readStream.ReadL( resultPtr, length ); + readStream.Close(); + CleanupStack::Pop( result ); + CleanupStack::Pop(); //readStream + return result; + } + +static void WriteLongTextColumn8L( + RDbRowSet aView, + TDbColNo aCol, + const TDesC8& aValue ) + { + RDbColWriteStream writeStream; + writeStream.OpenLC( aView, aCol ); + if ( &aValue ) + { + writeStream.WriteL( aValue ); + } + else + { + writeStream.WriteL( KNullDesC8 ); + } + writeStream.Close(); + CleanupStack::Pop( &writeStream ); + } + + +CNcdPurchaseHistoryDb* CNcdPurchaseHistoryDb::NewL( + const TDesC& aDbFilename ) + { + CNcdPurchaseHistoryDb* self = NewLC( aDbFilename ); + CleanupStack::Pop( self ); + return self; + } + +CNcdPurchaseHistoryDb* CNcdPurchaseHistoryDb::NewLC( + const TDesC& aDbFilename ) + { + CNcdPurchaseHistoryDb* self = + new ( ELeave ) CNcdPurchaseHistoryDb(); + CleanupClosePushL( *self ); + self->ConstructL( aDbFilename ); + return self; + } + +CNcdPurchaseHistoryDb::~CNcdPurchaseHistoryDb() + { + iDatabase.Close(); + delete iDbFilename; + iFs.Close(); + } + +void CNcdPurchaseHistoryDb::ReceiveMessage( + MCatalogsBaseMessage* aMessage, + TInt aFunctionNumber ) + { + DLTRACEIN(("")); + + DASSERT( aMessage ); + + // Now, we can be sure that rest of the time iMessage exists. + // This member variable is set for the CounterPartLost function. + iMessage = aMessage; + + TInt trapError( KErrNone ); + + switch( aFunctionNumber ) + { + case NcdNodeFunctionIds::ENcdPurchaseHistorySavePurchase: + DLTRACE(("Insert purchase event")); + TRAP( trapError, SavePurchaseRequestL( *aMessage ) ); + break; + + case NcdNodeFunctionIds::ENcdPurchaseHistorySavePurchaseWithOldIcon: + DLTRACE(("Insert purchase event with old icon")); + TRAP( trapError, SavePurchaseRequestL( *aMessage, EFalse ) ); + break; + + case NcdNodeFunctionIds::ENcdPurchaseHistoryRemovePurchase: + DLTRACE(("Remove purchase")); + TRAP( trapError, RemovePurchaseRequestL( *aMessage ) ); + break; + + case NcdNodeFunctionIds::ENcdPurchaseHistoryGetPurchaseIds: + DLTRACE(("Get purchase IDs")); + TRAP( trapError, GetPurchaseIdsRequestL( *aMessage ) ); + break; + + case NcdNodeFunctionIds::ENcdPurchaseHistoryGetPurchase: + DLTRACE(("Get purchase")); + TRAP( trapError, GetPurchaseRequestL( *aMessage, ETrue ) ); + break; + + case NcdNodeFunctionIds::ENcdPurchaseHistoryGetPurchaseNoIcon: + DLTRACE(("Get purchase")); + TRAP( trapError, GetPurchaseRequestL( *aMessage, EFalse ) ); + break; + + case NcdNodeFunctionIds::ENcdPurchaseHistoryEventCount: + DLTRACE(("Get purchase event count")); + TRAP( trapError, EventCountRequestL( *aMessage ) ); + break; + + case NcdNodeFunctionIds::ENcdRelease: + DLTRACE(("Release purchase history")); + ReleaseRequest( *aMessage ); + break; + + default: + break; + } + + if ( trapError != KErrNone ) + { + // Because something went wrong the complete has not been + // yet called for the message. + // So, inform the client about the error. + DLTRACE(("ERROR, Complete and release %d", trapError)); + + aMessage->CompleteAndRelease( trapError ); + } + + // Because the message should not be used after this, set it NULL. + // So, CounterPartLost function will know that no messages are + // waiting the response at the moment. + iMessage = NULL; + + DLTRACEOUT(("")); + } + +void CNcdPurchaseHistoryDb::CounterPartLost( + const MCatalogsSession& aSession ) + { + // This function may be called whenever -- when the message is waiting + // response or when the message does not exist. + // iMessage may be NULL here, because in the end of the + // ReceiveMessage it is set to NULL. The life time of the message + // ends shortly after CompleteAndRelease is called. + if ( iMessage != NULL ) + { + iMessage->CounterPartLost( aSession ); + } + } + +void CNcdPurchaseHistoryDb::ConstructL( const TDesC& aDbFilename ) + { + if ( aDbFilename == KNullDesC ) + { + User::Leave( KErrBadName ); + } + + User::LeaveIfError( iFs.Connect() ); + + // Set database name. + iDbFilename = aDbFilename.AllocL(); + BaflUtils::EnsurePathExistsL( iFs, *iDbFilename ); + TInt err = iDatabase.Open( iFs, *iDbFilename ); + + if ( err == KErrNotFound || err == KErrCorrupt ) + { + // Delete existing database file. This should happen only if + // database file is corrupted. Ignoring errors as database file + // may not be present. + BaflUtils::DeleteFile( iFs, *iDbFilename ); + + // See if the database can be found from another drive + TFindFile finder( iFs ); + CDir* dir = NULL; + + err = finder.FindWildByDir( *iDbFilename, KNullDesC, dir ); + + if ( err == KErrNone ) + { + // Move old database to correct folder and try to open it + CleanupStack::PushL( dir ); + CFileMan* fileman = CFileMan::NewL( iFs ); + CleanupStack::PushL( fileman ); + User::LeaveIfError( fileman->Move( finder.File(), + *iDbFilename, + CFileMan::EOverWrite ) ); + CleanupStack::PopAndDestroy( fileman ); + CleanupStack::PopAndDestroy( dir ); + + // NOTE: Error ignored. If folder contains files, + // it will not be deleted. + iFs.RmDir( finder.File() ); + + err = iDatabase.Open( iFs, *iDbFilename ); + if ( err == KErrNotFound || err == KErrCorrupt ) + { + BaflUtils::DeleteFile( iFs, *iDbFilename ); + // Trying to create the database + User::LeaveIfError( iDatabase.Create( iFs, *iDbFilename ) ); + User::LeaveIfError( + iDatabase.Execute( KSqlCreatePurchasesTable ) ); + User::LeaveIfError( + iDatabase.Execute( KSqlCreateEventCounterTable ) ); + } + else if ( err != KErrNone ) + { + User::Leave( err ); + } + } + else + { + // Trying to create the database + User::LeaveIfError( iDatabase.Create( iFs, *iDbFilename ) ); + User::LeaveIfError( + iDatabase.Execute( KSqlCreatePurchasesTable ) ); + User::LeaveIfError( + iDatabase.Execute( KSqlCreateEventCounterTable ) ); + } + } + else if ( err != KErrNone ) + { + User::Leave( err ); + } + + if ( iDatabase.IsDamaged() ) + { + // Database has been partly damaged. Try to recover it. + iDatabase.Recover(); + } + } + +CNcdPurchaseHistoryDb::CNcdPurchaseHistoryDb() + : CCatalogsCommunicable() + { + } + +void CNcdPurchaseHistoryDb::SavePurchaseL( + CNcdPurchaseDetails& aPurchase, + TBool aSaveIcon ) + { + DLTRACEIN(("")); + // Ensure that purchase history handling bugs in PCD! don't crash the client :) + ValidatePurchaseDetailsL( aPurchase ); + + CCatalogsBigDes* sqlStatement = CCatalogsBigDes::NewLC(); + + sqlStatement->AppendL( KSqlPurchaseUpdateStart ); + TBuf intBuf; + intBuf.Num( aPurchase.ClientUid().iUid ); + sqlStatement->AppendL( intBuf ); + sqlStatement->AppendL( KSqlPurchasesUpdateNamespace ); + AppendWithQuotesDuplicatedL( sqlStatement, aPurchase.Namespace() ); + sqlStatement->AppendL( KSqlPurchasesUpdateEntityId ); + AppendWithQuotesDuplicatedL( sqlStatement, aPurchase.EntityId() ); + sqlStatement->AppendL( KSqlPurchasesUpdatePurchaseTime ); + TBuf bigIntBuf; + bigIntBuf.Num( aPurchase.PurchaseTime().Int64() ); + sqlStatement->AppendL( bigIntBuf ); + sqlStatement->AppendL( KSqlPurchasesUpdateEnd ); + + TUint newEventCount = EventCountL() + 1; + + RDbView view; + CleanupClosePushL( view ); + + HBufC* sqlStatementBuf = sqlStatement->DesLC(); + User::LeaveIfError( + view.Prepare( iDatabase, TDbQuery( *sqlStatementBuf ) ) ); + CleanupStack::PopAndDestroy( sqlStatementBuf ); + + User::LeaveIfError( view.EvaluateAll() ); + view.FirstL(); + + if ( view.AtRow() ) + { + UpdatePurchaseL( view, aPurchase, newEventCount, aSaveIcon ); + } + else + { + view.Close(); + NewPurchaseL( aPurchase, newEventCount ); + } + + // Set new purchase count + SetEventCountL( newEventCount ); + + CleanupStack::PopAndDestroy( &view ); + CleanupStack::PopAndDestroy( sqlStatement ); + DLTRACEOUT(("")); + } + +void CNcdPurchaseHistoryDb::RemovePurchaseL( TUint aPurchaseId ) + { + if ( ! PurchaseExistsL( aPurchaseId ) ) + { + // Purchase didn't exist in the database. + User::Leave( KErrNotFound ); + } + + TInt maxLength = KSqlPurchasesDeleteStart().Length() + KMaxLengthOfInt + + KSqlPurchasesDeleteEnd().Length(); + + HBufC* sqlStatement = HBufC::NewLC( maxLength ); + TPtr statementPtr( sqlStatement->Des() ); + + statementPtr.Copy( KSqlPurchasesDeleteStart ); + statementPtr.AppendNum( aPurchaseId ); + statementPtr.Append( KSqlPurchasesDeleteEnd ); + + // Remove purchase from the database using event ID. + User::LeaveIfError( iDatabase.Execute( *sqlStatement ) ); + // Remove all unnecessary data from the database. + User::LeaveIfError( iDatabase.Compact() ); + + CleanupStack::PopAndDestroy( sqlStatement ); + } + +RArray CNcdPurchaseHistoryDb::PurchaseIdsL( + const CNcdPurchaseHistoryFilter& aFilter, + const TSortingOrder aSortingOrder ) + { + CCatalogsBigDes* sqlStatement = CCatalogsBigDes::NewLC(); + + // Construct the SQL query. + sqlStatement->AppendL( KSqlPurchasesStart ); + TBuf eventId; + eventId.Num( aFilter.EventId() ); + sqlStatement->AppendL( eventId ); + + if ( aFilter.Namespace().Compare( KNullDesC ) != 0 ) + { + sqlStatement->AppendL( KSqlPurchasesNamespace ); + AppendWithQuotesDuplicatedL( sqlStatement, aFilter.Namespace() ); + sqlStatement->AppendL( KSqlPurchasesEnd ); + } + + if ( aFilter.EntityId().Compare( KNullDesC ) != 0 ) + { + sqlStatement->AppendL( KSqlPurchasesEntityId ); + AppendWithQuotesDuplicatedL( sqlStatement, aFilter.EntityId() ); + sqlStatement->AppendL( KSqlPurchasesEnd ); + } + + if ( aFilter.ClientUids().Count() > 0 ) + { + TArray< TUid > uids = aFilter.ClientUids(); + TInt count = uids.Count(); + sqlStatement->AppendL( KSqlPurchasesClientUid ); + for ( TInt i = 0; i < count; i++ ) + { + TBuf clientUid; + clientUid.Num( uids[i].iUid ); + sqlStatement->AppendL( clientUid ); + if ( i + 1 < count ) + { + // More UIDs in the filter. + sqlStatement->AppendL( KSqlPurchasesOrClientUid ); + } + } + sqlStatement->AppendL( KSqlPurchasesEndClientUid ); + } + + switch ( aSortingOrder ) + { + case CNcdPurchaseHistoryDb::ENewestFirst: + sqlStatement->AppendL( KSqlPurchasesOrderNewestFirst ); + break; + case CNcdPurchaseHistoryDb::EOldestFirst: + sqlStatement->AppendL( KSqlPurchasesOrderOldestFirst ); + break; + case CNcdPurchaseHistoryDb::ENone: + default: + break; + } + + RDbView view; + CleanupClosePushL( view ); + + HBufC* sqlStatementBuf = sqlStatement->DesLC(); + User::LeaveIfError( + view.Prepare( iDatabase, TDbQuery( *sqlStatementBuf ) ) ); + CleanupStack::PopAndDestroy( sqlStatementBuf ); + + User::LeaveIfError( view.EvaluateAll() ); + view.FirstL(); + + RArray purchaseIds; + CleanupClosePushL( purchaseIds ); + + while( view.AtRow() ) + { + view.GetL(); + purchaseIds.Append( + view.ColUint32( CNcdPurchaseHistoryDb::EPurchaseId ) ); + view.NextL(); + } + + CleanupStack::Pop( &purchaseIds ); + CleanupStack::PopAndDestroy( &view ); + CleanupStack::PopAndDestroy( sqlStatement ); + + return purchaseIds; + } + +CNcdPurchaseDetails* CNcdPurchaseHistoryDb::PurchaseL( + TUint aPurchaseId, + TBool aLoadIcon ) + { + TInt maxLength = KSqlPurchaseByPurchaseIdStart().Length() + + KMaxLengthOfInt + + KSqlPurchaseByPurchaseIdEnd().Length(); + + HBufC* sqlStatement = HBufC::NewLC( maxLength ); + TPtr statementPtr( sqlStatement->Des() ); + + statementPtr.Copy( KSqlPurchaseByPurchaseIdStart ); + statementPtr.AppendNum( aPurchaseId ); + statementPtr.Append( KSqlPurchaseByPurchaseIdEnd ); + + RDbView view; + CleanupClosePushL( view ); + + User::LeaveIfError( + view.Prepare( iDatabase, TDbQuery( *sqlStatement ) ) ); + + User::LeaveIfError( view.EvaluateAll() ); + view.FirstL(); + + CNcdPurchaseDetails* details = NULL; + + if ( view.AtRow() ) + { + view.GetL(); + + details = CNcdPurchaseDetails::NewLC(); + + details->SetClientUid( TUid::Uid( view.ColInt32( EClientUid ) ) ); + details->SetNamespace( ReadLongTextColumnL( view, ENamespace ) ); + details->SetEntityId( ReadLongTextColumnL( view, EEntityId ) ); + details->SetItemName( ReadLongTextColumnL( view, EItemName ) ); + details->SetItemPurpose( view.ColUint32( EItemPurpose ) ); + details->SetCatalogSourceName( + ReadLongTextColumnL( view, ECatalogName ) ); + + // Get download info buffer from database + HBufC8* downloadBuf = ReadLongTextColumn8L( view, EDownloadInfo ); + CleanupStack::PushL( downloadBuf ); + + if ( downloadBuf->Length() > 0 ) + { + // There is some data. + RDesReadStream readStream( *downloadBuf ); + CleanupClosePushL( readStream ); + + // Get count of download infos. + TInt count = readStream.ReadInt32L(); + for ( TInt i = 0; i < count; i++ ) + { + // Internalize download infos from the buffer. + + CNcdPurchaseDownloadInfo* info = + CNcdPurchaseDownloadInfo::NewLC(); + + info->InternalizeL( readStream ); + details->AddDownloadInfoL( info ); + + CleanupStack::Pop( info ); + } + + CleanupStack::PopAndDestroy( &readStream ); + } + + CleanupStack::PopAndDestroy( downloadBuf ); + + details->SetPurchaseOptionId( + ReadLongTextColumnL( view, EPurchaseOptionId ) ); + details->SetPurchaseOptionName( + ReadLongTextColumnL( view, EPurchaseOptionName ) ); + details->SetPurchaseOptionPrice( + ReadLongTextColumnL( view, EPurchaseOptionPrice ) ); + details->SetFinalPrice( ReadLongTextColumnL( view, EFinalPrice ) ); + details->SetPaymentMethodName( + ReadLongTextColumnL( view, EPaymentMethodName ) ); + details->SetPurchaseTime( view.ColInt64( EPurchaseTime ) ); + + HBufC8* filesBuf = ReadLongTextColumn8L( view, EDownloadedFiles ); + CleanupStack::PushL( filesBuf ); + CDesCArray* downloadedFiles = + new (ELeave) CDesCArrayFlat( KListGranularity ); + CleanupStack::PushL( downloadedFiles ); + + if ( filesBuf->Length() > 0 ) + { + // There is some data. + RDesReadStream readStream( *filesBuf ); + CleanupClosePushL( readStream ); + + TInt filesCount = readStream.ReadInt32L(); + for ( TInt i = 0; i < filesCount; i++ ) + { + HBufC* buf = NULL; + InternalizeDesL( buf, readStream ); + CleanupStack::PushL( buf ); + downloadedFiles->AppendL( *buf ); + CleanupStack::PopAndDestroy( buf ); + } + + CleanupStack::PopAndDestroy( &readStream ); + } + + details->SetDownloadedFiles( downloadedFiles ); + CleanupStack::Pop( downloadedFiles ); + CleanupStack::PopAndDestroy( filesBuf ); + + // Get install info buffer from database + HBufC8* installBuf = ReadLongTextColumn8L( view, EFileInstallInfos ); + CleanupStack::PushL( installBuf ); + + if ( installBuf->Length() > 0 ) + { + // There is some data. + RDesReadStream readStream( *installBuf ); + CleanupClosePushL( readStream ); + + // Get count of install infos. + TInt count = readStream.ReadInt32L(); + for ( TInt i = 0; i < count; i++ ) + { + // Internalize install infos from the buffer. + + CNcdPurchaseInstallInfo* info = + CNcdPurchaseInstallInfo::NewLC(); + + info->InternalizeL( readStream ); + details->AddInstallInfoL( info ); + + CleanupStack::Pop( info ); + } + + CleanupStack::PopAndDestroy( &readStream ); + } + + CleanupStack::PopAndDestroy( installBuf ); + + if ( aLoadIcon ) + { + DLTRACE(("Loading icon from the purchase history")); + details->SetIcon( ReadLongTextColumn8L( view, EIcon ) ); + } + details->SetDownloadAccessPoint( ReadLongTextColumnL( view, EDownloadAccessPoint ) ); + details->SetDescription( ReadLongTextColumnL( view, EDescription ) ); + details->SetVersion( ReadLongTextColumnL( view, EVersion ) ); + details->SetServerUri( ReadLongTextColumnL( view, EServerUri ) ); + details->SetItemType( (MNcdPurchaseDetails::TItemType)view.ColInt32( EItemType ) ); + details->SetTotalContentSize( view.ColInt32( ETotalContentSize ) ); + details->SetOriginNodeId( ReadLongTextColumnL( view, EOriginNodeId ) ); + details->SetLastOperationTime( view.ColInt64( ELastOperationTime ) ); + details->SetLastOperationErrorCode( view.ColInt32( ELastOperationErrorCode ) ); + details->SetHasIcon( view.ColInt32( EHasIcon ) ); + + // Get attributes buffer from database + HBufC8* attributesBuf = ReadLongTextColumn8L( view, EAttributes ); + CleanupStack::PushL( attributesBuf ); + + if ( attributesBuf->Length() > 0 ) + { + // There is some data. + RDesReadStream readStream( *attributesBuf ); + CleanupClosePushL( readStream ); + details->InternalizeAttributesL( readStream ); + CleanupStack::PopAndDestroy( &readStream ); + } + + CleanupStack::PopAndDestroy( attributesBuf ); + } + + if ( ! details ) + { + User::Leave( KErrNotFound ); + } + + // Ensure that purchase history handling bugs in PCD! don't crash the client :) + ValidatePurchaseDetailsL( *details ); + + CleanupStack::Pop( details ); + CleanupStack::PopAndDestroy( &view ); + CleanupStack::PopAndDestroy( sqlStatement ); + + return details; + } + +TUint CNcdPurchaseHistoryDb::EventCountL() + { + TUint eventCount = 0; + + RDbView view; + CleanupClosePushL( view ); + + User::LeaveIfError( view.Prepare( + iDatabase, + TDbQuery( KSqlCurrentEventCount ), + RDbView::EReadOnly ) ); + + view.EvaluateAll(); + view.FirstL(); + + if ( view.AtRow() ) + { + view.GetL(); + eventCount = view.ColUint32( KEventCounterColumnNumber ); + } + + CleanupStack::PopAndDestroy( &view ); + + return eventCount; + } + +void CNcdPurchaseHistoryDb::SavePurchaseRequestL( MCatalogsBaseMessage& aMessage, + TBool aSaveIcon ) + { + TInt inputLength = aMessage.InputLength(); + User::LeaveIfError( inputLength ); + + HBufC8* message = HBufC8::NewLC( inputLength ); + TPtr8 ptr = message->Des(); + User::LeaveIfError( aMessage.ReadInput( ptr ) ); + + CNcdPurchaseDetails* details = CNcdPurchaseDetails::NewLC(); + + RDesReadStream stream( *message ); + CleanupClosePushL( stream ); + + details->InternalizeL( stream ); + + CleanupStack::PopAndDestroy( &stream ); + + SavePurchaseL( *details, aSaveIcon ); + + CleanupStack::PopAndDestroy( details ); + CleanupStack::PopAndDestroy( message ); + + aMessage.CompleteAndRelease( KErrNone ); + } + +void CNcdPurchaseHistoryDb::RemovePurchaseRequestL( MCatalogsBaseMessage& aMessage ) + { + TInt inputLength = aMessage.InputLength(); + User::LeaveIfError( inputLength ); + + HBufC8* message = HBufC8::NewLC( inputLength ); + TPtr8 ptr = message->Des(); + User::LeaveIfError( aMessage.ReadInput( ptr ) ); + + TUint purchaseId = Des8ToUint( *message ); + + CleanupStack::PopAndDestroy( message ); + + RemovePurchaseL( purchaseId ); + + aMessage.CompleteAndRelease( KErrNone ); + } + +void CNcdPurchaseHistoryDb::GetPurchaseIdsRequestL( MCatalogsBaseMessage& aMessage ) + { + TInt inputLength = aMessage.InputLength(); + User::LeaveIfError( inputLength ); + + HBufC8* message = HBufC8::NewLC( inputLength ); + TPtr8 ptr = message->Des(); + User::LeaveIfError( aMessage.ReadInput( ptr ) ); + + CNcdPurchaseHistoryFilter* filter = CNcdPurchaseHistoryFilter::NewLC(); + + RDesReadStream stream( *message ); + CleanupClosePushL( stream ); + + filter->InternalizeL( stream ); + + CleanupStack::PopAndDestroy( &stream ); + + RArray purchaseIds = PurchaseIdsL( *filter, ENewestFirst ); + + CleanupStack::PopAndDestroy( filter ); + CleanupStack::PopAndDestroy( message ); + + CleanupClosePushL( purchaseIds ); + + TUint count = purchaseIds.Count(); + + message = HBufC8::NewLC( ( count + 1 ) * sizeof(TUint) ); + + HBufC8* temp = UintToDes8LC( count ); + message->Des().Copy( *temp ); + CleanupStack::PopAndDestroy( temp ); + + for ( TInt i = 0; i < count; i++ ) + { + temp = UintToDes8LC( purchaseIds[i] ); + message->Des().Append( *temp ); + CleanupStack::PopAndDestroy( temp ); + } + + aMessage.CompleteAndReleaseL( *message, KErrNone ); + + CleanupStack::PopAndDestroy( message ); + + CleanupStack::PopAndDestroy( &purchaseIds ); + } + +void CNcdPurchaseHistoryDb::GetPurchaseRequestL( + MCatalogsBaseMessage& aMessage, + TBool aLoadIcon ) + { + DLTRACEIN(("aLoadIcon: %d", aLoadIcon)); + TInt inputLength = aMessage.InputLength(); + User::LeaveIfError( inputLength ); + + HBufC8* message = HBufC8::NewLC( inputLength ); + TPtr8 ptr = message->Des(); + User::LeaveIfError( aMessage.ReadInput( ptr ) ); + + TUint purchaseId = Des8ToUint( *message ); + + CleanupStack::PopAndDestroy( message ); + + CNcdPurchaseDetails* details = PurchaseL( purchaseId, aLoadIcon ); + if ( details ) + { + CleanupStack::PushL( details ); + + CBufBase* buf = CBufFlat::NewL( KBufExpandSize ); + CleanupStack::PushL( buf ); + + RBufWriteStream stream( *buf ); + CleanupClosePushL( stream ); + + details->ExternalizeL( stream ); + + CleanupStack::PopAndDestroy( &stream ); + + aMessage.CompleteAndReleaseL( buf->Ptr( 0 ), KErrNone ); + + CleanupStack::PopAndDestroy( buf ); + CleanupStack::PopAndDestroy( details ); + } + else + { + aMessage.CompleteAndRelease( KErrNotFound ); + } + } + +void CNcdPurchaseHistoryDb::EventCountRequestL( MCatalogsBaseMessage& aMessage ) + { + TUint count = EventCountL(); + + HBufC8* message = UintToDes8LC( count ); + + aMessage.CompleteAndReleaseL( *message, KErrNone ); + + CleanupStack::PopAndDestroy( message ); + } + +void CNcdPurchaseHistoryDb::ReleaseRequest( MCatalogsBaseMessage& aMessage ) + { + DLTRACEIN(("")); + + // Decrease the reference count for this object. + // When the reference count reaches zero, this object will be destroyed + // and removed from the session. + MCatalogsSession& requestSession( aMessage.Session() ); + TInt handle( aMessage.Handle() ); + + // Send complete information back to proxy. + aMessage.CompleteAndRelease( KErrNone ); + + // Remove this object from the session. + requestSession.RemoveObject( handle ); + + DLTRACEOUT(("")); + } + +void CNcdPurchaseHistoryDb::NewPurchaseL( + CNcdPurchaseDetails& aPurchase, + TUint aNewEventCount ) + { + // Construct database view. + RDbView view; + CleanupClosePushL( view ); + User::LeaveIfError( view.Prepare( + iDatabase, + TDbQuery( KSqlPurchasesAllNone ), + RDbView::EInsertOnly ) ); + view.InsertL(); + + // Set initial value for EHasIcon here. This is needed because in SetPurchaseViewL + // the flag is never unset after being set to true. The reason for this is that + // the flag is unkwnown to PCD! Adapter and when it syncs PH data with the engine + // it cannot set the flag correctly. Therefore the flag is always kept as true + // regardless of what PCD! Adapter sends in the sync. This is correct because + // once set, the icon data is never removed from the purchase detail entry. + view.SetColL( EHasIcon, EFalse ); + + SetPurchaseViewL( view, aPurchase, aNewEventCount, ETrue ); + + + // Insert purchase into the database + view.PutL(); + view.Close(); + CleanupStack::PopAndDestroy( &view ); + } + + +void CNcdPurchaseHistoryDb::UpdatePurchaseL( + RDbRowSet& aView, + CNcdPurchaseDetails& aPurchase, + TUint aNewEventCount, + TBool aSaveIcon ) + { + aView.UpdateL(); + + SetPurchaseViewL( aView, aPurchase, aNewEventCount, aSaveIcon ); + + // Update purchase details. + aView.PutL(); + } + + +void CNcdPurchaseHistoryDb::SetPurchaseViewL( + RDbRowSet& aView, + CNcdPurchaseDetails& aPurchase, + TUint aNewEventCount, + TBool aSaveIcon ) + { + aView.SetColL( EEventId, aNewEventCount ); + aView.SetColL( EClientUid, aPurchase.ClientUid().iUid ); + WriteLongTextColumnL( aView, ENamespace, aPurchase.Namespace() ); + WriteLongTextColumnL( aView, EEntityId, aPurchase.EntityId() ); + WriteLongTextColumnL( aView, EItemName, aPurchase.ItemName() ); + aView.SetColL( EItemPurpose, aPurchase.ItemPurpose() ); + WriteLongTextColumnL( + aView, + ECatalogName, + aPurchase.CatalogSourceName() ); + + CBufBase* downloadBuf = CBufFlat::NewL( KBufExpandSize ); + CleanupStack::PushL( downloadBuf ); + + RBufWriteStream downloadStream( *downloadBuf ); + CleanupClosePushL( downloadStream ); + + TInt downloadCount = aPurchase.DownloadInfoCount(); + downloadStream.WriteInt32L( downloadCount ); + for ( TInt i = 0; i < downloadCount; i++ ) + { + // Download information present. + + CNcdPurchaseDownloadInfo& info = aPurchase.DownloadInfo( i ); + info.ExternalizeL( downloadStream ); + } + + CleanupStack::PopAndDestroy( &downloadStream ); + TPtr8 downloadPtr = downloadBuf->Ptr( 0 ); + WriteLongTextColumn8L( aView, EDownloadInfo, downloadPtr ); + CleanupStack::PopAndDestroy( downloadBuf ); + + WriteLongTextColumnL( aView, EPurchaseOptionId, aPurchase.PurchaseOptionId() ); + WriteLongTextColumnL( aView, EPurchaseOptionName, aPurchase.PurchaseOptionName() ); + WriteLongTextColumnL( aView, EPurchaseOptionPrice, aPurchase.PurchaseOptionPrice() ); + WriteLongTextColumnL( aView, EFinalPrice, aPurchase.FinalPrice() ); + WriteLongTextColumnL( aView, EPaymentMethodName, aPurchase.PaymentMethodName() ); + aView.SetColL( EPurchaseTime, aPurchase.PurchaseTime().Int64() ); + + const MDesCArray& downloadedFiles = aPurchase.DownloadedFiles(); + + CBufBase* filesBuf = CBufFlat::NewL( KBufExpandSize ); + CleanupStack::PushL( filesBuf ); + + RBufWriteStream filesStream( *filesBuf ); + CleanupClosePushL( filesStream ); + + TInt filesCount = downloadedFiles.MdcaCount(); + filesStream.WriteInt32L( filesCount ); + for ( TInt i = 0; i < filesCount; i++ ) + { + // Downloaded files present. + + ExternalizeDesL( downloadedFiles.MdcaPoint( i ), filesStream ); + } + + CleanupStack::PopAndDestroy( &filesStream ); + TPtr8 filesPtr = filesBuf->Ptr( 0 ); + WriteLongTextColumn8L( aView, EDownloadedFiles, filesPtr ); + CleanupStack::PopAndDestroy( filesBuf ); + + CBufBase* installBuf = CBufFlat::NewL( KBufExpandSize ); + CleanupStack::PushL( installBuf ); + + RBufWriteStream installStream( *installBuf ); + CleanupClosePushL( installStream ); + + TInt installCount = aPurchase.InstallInfoCount(); + installStream.WriteInt32L( installCount ); + for ( TInt i = 0; i < installCount; i++ ) + { + // Install information present. + + CNcdPurchaseInstallInfo& info = aPurchase.InstallInfo( i ); + info.ExternalizeL( installStream ); + } + + CleanupStack::PopAndDestroy( &installStream ); + TPtr8 installPtr = installBuf->Ptr( 0 ); + WriteLongTextColumn8L( aView, EFileInstallInfos, installPtr ); + CleanupStack::PopAndDestroy( installBuf ); + + if ( aSaveIcon ) + { + DLTRACE(("Saving the icon to purchase history")); + WriteLongTextColumn8L( aView, EIcon, aPurchase.Icon() ); + } + WriteLongTextColumnL( + aView, + EDownloadAccessPoint, + aPurchase.DownloadAccessPoint() ); + WriteLongTextColumnL( aView, EDescription, aPurchase.Description() ); + WriteLongTextColumnL( aView, EVersion, aPurchase.Version() ); + WriteLongTextColumnL( aView, EServerUri, aPurchase.ServerUri() ); + aView.SetColL( EItemType, aPurchase.ItemType() ); + aView.SetColL( ETotalContentSize, aPurchase.TotalContentSize() ); + WriteLongTextColumnL( aView, EOriginNodeId, aPurchase.OriginNodeId() ); + aView.SetColL( ELastOperationTime, aPurchase.LastOperationTime().Int64() ); + aView.SetColL( ELastOperationErrorCode, aPurchase.LastOperationErrorCode() ); + + if( aPurchase.HasIcon() ) + { + // HasIcon flag can be set but never unset. + aView.SetColL( EHasIcon, ETrue ); + } + + // Externalize attributes + RCatalogsBufferWriter attributeWriter; + attributeWriter.OpenLC(); + aPurchase.ExternalizeAttributesL( attributeWriter() ); + WriteLongTextColumn8L( aView, EAttributes, attributeWriter.PtrL() ); + CleanupStack::PopAndDestroy( &attributeWriter ); + + } + +void CNcdPurchaseHistoryDb::SetEventCountL( TUint aEventCount ) + { + // Remove old event count from the database. + User::LeaveIfError( iDatabase.Execute( KSqlEventCountDelete ) ); + + // Construct database view. + RDbView view; + User::LeaveIfError( view.Prepare( + iDatabase, + TDbQuery( KSqlCurrentEventCount ), + RDbView::EInsertOnly ) ); + view.InsertL(); + + view.SetColL( KEventCounterColumnNumber, aEventCount ); + + // Insert event count into the database + view.PutL(); + view.Close(); + + // Remove all unnecessary data from the database. + User::LeaveIfError( iDatabase.Compact() ); + } + +TBool CNcdPurchaseHistoryDb::PurchaseExistsL( TUint aPurchaseId ) + { + TInt maxLength = KSqlPurchaseByPurchaseIdStart().Length() + + KMaxLengthOfInt + + KSqlPurchaseByPurchaseIdEnd().Length(); + + HBufC* sqlStatement = HBufC::NewLC( maxLength ); + TPtr statementPtr( sqlStatement->Des() ); + + statementPtr.Copy( KSqlPurchaseByPurchaseIdStart ); + statementPtr.AppendNum( aPurchaseId ); + statementPtr.Append( KSqlPurchaseByPurchaseIdEnd ); + + RDbView view; + CleanupClosePushL( view ); + + User::LeaveIfError( + view.Prepare( iDatabase, TDbQuery( *sqlStatement ) ) ); + + User::LeaveIfError( view.EvaluateAll() ); + view.FirstL(); + + TBool exists = EFalse; + + if ( view.AtRow() ) + { + exists = ETrue; + } + + CleanupStack::PopAndDestroy( &view ); + CleanupStack::PopAndDestroy( sqlStatement ); + + return exists; + } + + +void CNcdPurchaseHistoryDb::ValidatePurchaseDetailsL( + CNcdPurchaseDetails& aDetails ) + { + DLTRACEIN(("")); + + TInt dlInfoCount = aDetails.DownloadInfoCount(); + + // Add/remove filepaths so that there's as many filepaths as there are + // download infos. Download infos determine the real count because they + // are received from the protocol + TInt filePathDiff = dlInfoCount - aDetails.DownloadedFiles().MdcaCount(); + if ( filePathDiff > 0 ) + { + DLTRACE(("Adding %d empty filepaths", filePathDiff )); + while ( filePathDiff-- ) + { + aDetails.AddDownloadedFileL( KNullDesC() ); + } + } + else if ( filePathDiff < 0 ) // This shouldn't really ever happen + { + DLTRACE(("Removing %d filepaths", -filePathDiff )); + // Start from the end + TInt index = aDetails.DownloadedFiles().MdcaCount() - 1; + + filePathDiff = index + filePathDiff; + for ( ; index > filePathDiff; index-- ) + { + aDetails.RemoveDownloadedFile( index ); + } + } + + // install info count must be <= download info count + TInt installDiff = dlInfoCount - aDetails.InstallInfoCount(); + if ( installDiff < 0 ) // This shouldn't really ever happen + { + DLTRACE(("Removing %d install infos", -installDiff )); + // Start from the end + TInt index = aDetails.InstallInfoCount() - 1; + + installDiff = index + installDiff; + for ( ; index > installDiff; index-- ) + { + aDetails.RemoveInstallInfo( index ); + } + } + } +