--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ncdengine/provider/purchasehistory/src/ncdpurchasehistorydbimpl.cpp Thu Dec 17 08:51:10 2009 +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 <eikenv.h>
+#include <f32file.h>
+#include <bautils.h>
+#include <s32mem.h>
+
+#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<KMaxLengthOfInt> 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<KMaxLengthOfTint64> 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<TUint> CNcdPurchaseHistoryDb::PurchaseIdsL(
+ const CNcdPurchaseHistoryFilter& aFilter,
+ const TSortingOrder aSortingOrder )
+ {
+ CCatalogsBigDes* sqlStatement = CCatalogsBigDes::NewLC();
+
+ // Construct the SQL query.
+ sqlStatement->AppendL( KSqlPurchasesStart );
+ TBuf<KMaxLengthOfInt> 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<KMaxLengthOfInt> 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<TUint> 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<TUint> 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 );
+ }
+ }
+ }
+