--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/authorisation/userpromptservice/database/source/upsdbw.cpp Thu Sep 10 14:01:51 2009 +0300
@@ -0,0 +1,593 @@
+/*
+* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "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:
+* Implements a writable interface for UPS Database.
+*
+*/
+
+
+/**
+ @file
+ @internalTechnology
+ @prototype
+*/
+
+#include "upsdbw.h"
+#include "upscommon.h"
+
+using namespace UserPromptService;
+
+CDecisionDbW::CDecisionDbW()
+/**
+ Constructor for writable decision database object
+ */
+ {
+
+ }
+
+
+CDecisionDbW::~CDecisionDbW()
+/**
+ Destructor for writable decision database object
+ */
+ {
+ iDatabase.Close();
+ delete iDbName;
+ delete iStore;
+ }
+
+
+EXPORT_C CDecisionDbW* CDecisionDbW::NewL(const TDesC& aDbName, RFs& aFs)
+/**
+ Creates a writable decision database object and connects to the database.
+ If the database does not exist or is corrupted, a new decision database is created.
+ The function leaves, if creation of the object or connection to the database fail.
+
+ @param aDbName The path of the decision database
+ @param aFs Handle to the file server session
+
+ @return A pointer to the newly allocated database object, if creation and connection are successful.
+ */
+ {
+ CDecisionDbW* self = CDecisionDbW::NewLC(aDbName, aFs);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+
+EXPORT_C CDecisionDbW* CDecisionDbW::NewLC(const TDesC& aDbName, RFs& aFs)
+/**
+ Creates a writable decision database object and connects to the database.
+ If the database does not exist or is corrupted, a new decision database is created.
+ The function leaves, if creation of the object or connection to the database fail.
+
+ @param aDbName The path of the decision database
+ param aFs Handle to the file server session
+
+ @return A pointer to the newly allocated database object, if creation and connection
+ are successful. The pointer is also put onto the cleanup stack.
+ */
+ {
+ CDecisionDbW* self = new (ELeave) CDecisionDbW();
+ CleanupStack::PushL(self);
+ self->ConstructL(aDbName, aFs);
+ return self;
+ }
+
+
+void CDecisionDbW::ConstructL(const TDesC& aDbName, RFs& aFs)
+/**
+ Second phase constructor for the decision database object. Connects to the decision database.
+ If the database does not existed or is corrupted, creates a new one. The function leaves,
+ if both database connection and creation fail.
+
+ @param aDbName The path of the decision database
+ @param aFs Handle to the file server session
+ */
+ {
+ //Construct the name of UPS decision database
+ iDbName = aDbName.AllocL();
+
+ //First try to open the decision database
+ TRAPD(error,OpenDatabaseL(aFs));
+
+ if((error == KErrNoMemory) || (error == KErrDiskFull))
+ {
+ User::Leave(error);
+ }
+
+ // Makes sure that DB file is writable. (DEF122590)
+ aFs.SetAtt(iDbName->Des(),0, KEntryAttReadOnly);
+
+ if(error != KErrNone)
+ {
+ DEBUG_PRINTF2(_L("%S database file does not exist or is corrupted."), iDbName);
+ //The decision database does not exist or is corrupted, create a new one
+ CreateDatabaseL(aFs);
+
+ //Create a decision table in the decision database
+ CreateTableL();
+
+ //Create an index on the decision table
+ CreateIndexL();
+ }
+ }
+
+
+void CDecisionDbW::OpenDatabaseL(RFs& aFs)
+/**
+ Opens the decision database.
+ @param aFs Handle to the file server session
+ */
+ {
+ DEBUG_PRINTF2(_L("%S database file is being opened."), iDbName);
+
+ iStore = CPermanentFileStore::OpenL(aFs, *iDbName, EFileRead|EFileWrite);
+ iDatabase.OpenL(iStore, iStore->Root());
+
+ //Database does exist. However, make sure that the decision table also exists
+ CDbTableNames *tables = iDatabase.TableNamesL();
+ CleanupStack::PushL(tables);
+ if(1 != tables->Count())
+ {
+ DEBUG_PRINTF(_L("The decision table could not be found in the database file!"));
+ User::Leave(KErrNotFound);
+ }
+ CleanupStack::PopAndDestroy(tables);
+
+ //OK. The decision table does exist. What about the decision index?
+ CDbIndexNames *indexes = iDatabase.IndexNamesL(KDecisionTable);
+ CleanupStack::PushL(indexes);
+ if(2 != indexes->Count())
+ {
+ DEBUG_PRINTF(_L("The index on the decision table is missing!"));
+ User::Leave(KErrNotFound);
+ }
+ CleanupStack::PopAndDestroy(indexes);
+
+ if(iDatabase.IsDamaged())
+ {
+ User::LeaveIfError(iDatabase.Recover());
+ }
+
+ DEBUG_PRINTF2(_L("%S database file has been opened successfully."), iDbName);
+ }
+
+
+void CDecisionDbW::CreateDatabaseL(RFs& aFs)
+/**
+ Creates a new, empty store database.
+ @param aFs Handle to the file server session
+ */
+ {
+ DEBUG_PRINTF2(_L("%S database is being created."), iDbName);
+
+ if(iStore)
+ {//if database file exists but the table does not, iStore has already been allocated.
+ delete iStore;
+ iStore = 0;
+ iDatabase.Close();
+ }
+
+ //Create a file store
+ iStore = CPermanentFileStore::ReplaceL(aFs, *iDbName, EFileRead|EFileWrite);
+ iStore->SetTypeL(iStore->Layout());
+
+ //Create UPS Decision Database in the file store
+ TStreamId sId = iDatabase.CreateL(iStore);
+ iStore->SetRootL(sId);
+
+ //Commit the database creation
+ iStore->CommitL();
+
+ DEBUG_PRINTF2(_L("%S database has been created successfully."), iDbName);
+ }
+
+
+void CDecisionDbW::CreateTableL()
+/**
+ Creates the decision table in the decision database.
+ */
+ {
+ DEBUG_PRINTF(_L("The Ups decision table is being created."));
+
+ // Create a table definition
+ CDbColSet* columns=CDbColSet::NewLC();
+
+ // add the columns to the definition
+ TDbCol clientSid(KColClientSid,EDbColUint32);
+ clientSid.iAttributes = TDbCol::ENotNull;
+ columns->AddL(clientSid);
+
+ TDbCol evaluatorId(KColEvaluatorId,EDbColUint32);
+ evaluatorId.iAttributes = TDbCol::ENotNull;
+ columns->AddL(evaluatorId);
+
+ TDbCol serviceId(KColServiceId,EDbColUint32);
+ serviceId.iAttributes=TDbCol::ENotNull;
+ columns->AddL(serviceId);
+
+ TDbCol serverSid(KColServerSid,EDbColUint32);
+ serverSid.iAttributes=TDbCol::ENotNull;
+ columns->AddL(serverSid);
+
+ TDbCol fingerprint(KColFingerprint,EDbColText8,KUpsMaxFingerprintLength );
+ columns->AddL(fingerprint);
+
+ TDbCol clientEntity(KColClientEntity,EDbColText8,KUpsMaxClientEntityLength);
+ columns->AddL(clientEntity);
+
+ TDbCol description(KColDescription,EDbColLongText);
+ columns->AddL(description);
+
+ TDbCol result(KColResult,EDbColInt8);
+ result.iAttributes=TDbCol::ENotNull;
+ columns->AddL(result);
+
+ TDbCol evaluatorInfo(KColEvaluatorInfo,EDbColUint32);
+ columns->AddL(evaluatorInfo);
+
+ TDbCol policyVersion(KColMajorPolicyVersion,EDbColUint16);
+ policyVersion.iAttributes=TDbCol::ENotNull;
+ columns->AddL(policyVersion);
+
+ TDbCol recordId(KColRecordId,EDbColUint32);
+ recordId.iAttributes=TDbCol::ENotNull|TDbCol::EAutoIncrement;
+ columns->AddL(recordId);
+
+ // Create a table with the table definition
+ User::LeaveIfError(iDatabase.CreateTable(KDecisionTable,*columns));
+
+ // cleanup the column set
+ CleanupStack::PopAndDestroy(columns);
+
+ DEBUG_PRINTF(_L("The Ups decision table has been created successfully."));
+ }
+
+
+void CDecisionDbW::CreateIndexL()
+/**
+ Creates an index on the decision table and makes it unique.
+ */
+ {
+ DEBUG_PRINTF(_L("The Ups decision index is being created on the decision table."));
+
+ // create the index key
+ CDbKey* key=CDbKey::NewLC();
+
+ // add the key columns
+ key->AddL(TDbKeyCol(KColClientSid));
+ key->AddL(TDbKeyCol(KColEvaluatorId));
+ key->AddL(TDbKeyCol(KColServiceId));
+ key->AddL(TDbKeyCol(KColServerSid));
+ key->AddL(TDbKeyCol(KColFingerprint));
+ key->AddL(TDbKeyCol(KColClientEntity));
+ key->AddL(TDbKeyCol(KColMajorPolicyVersion));
+
+ //Make the index key unique
+ key->MakeUnique();
+
+ // create the primary index
+ User::LeaveIfError(iDatabase.CreateIndex(KPrimaryIndex,KDecisionTable,*key));
+
+ //Now create the second index on the record id
+ key->Clear();
+ // add the record id column
+ key->AddL(TDbKeyCol(KColRecordId));
+ key->MakeUnique();
+
+ // create the record id index
+ User::LeaveIfError(iDatabase.CreateIndex(KRecordIdIndex,KDecisionTable,*key));
+
+ // cleanup the key
+ CleanupStack::PopAndDestroy(key);
+
+ DEBUG_PRINTF(_L("The Ups decision index has been created successfully."));
+ }
+
+
+EXPORT_C void CDecisionDbW::DeleteDatabaseL(RFs& aFs)
+/**
+ Delete the decision database completely.
+ @param aFs Handle to the file server session
+ */
+ {
+ DEBUG_PRINTF2(_L("%S database is being deleted."),iDbName);
+
+ iDatabase.Close();
+ delete iStore;
+ iStore = 0;
+ User::LeaveIfError(aFs.Delete(*iDbName));
+
+ DEBUG_PRINTF2(_L("%S database has been deleted successfully."),iDbName);
+ }
+
+
+void CDecisionDbW::UpdateCurrentRowL(RDbTable& aTable, CDecisionRecord& aRecord)
+/**
+ Updates the current row of the rowset by using the values the provided decision record.
+
+ @param aTable A table object which provides access to table data as a rowset.
+ @param aRecord A decision record object used to update the current row.
+ */
+ {
+ //Use CDbColSet to set fields
+ CDbColSet* colSet = aTable.ColSetL();
+ CleanupStack::PushL(colSet);
+
+ //Set the fields of the empty row
+ aTable.SetColL(colSet->ColNo(KColClientSid) ,(TUint32)aRecord.iClientSid.iId);
+ aTable.SetColL(colSet->ColNo(KColEvaluatorId),(TUint32)aRecord.iEvaluatorId.iUid);
+ aTable.SetColL(colSet->ColNo(KColServiceId) ,(TUint32)aRecord.iServiceId.iUid);
+ aTable.SetColL(colSet->ColNo(KColServerSid) ,(TUint32)aRecord.iServerSid.iId);
+
+ //Fingerprint may be null
+ if(aRecord.iFingerprint.Length() != 0)
+ {
+ aTable.SetColL(colSet->ColNo(KColFingerprint),aRecord.iFingerprint);
+ }
+
+ //ClientEntity may be null
+ if(aRecord.iClientEntity.Length() != 0)
+ {
+ aTable.SetColL(colSet->ColNo(KColClientEntity),aRecord.iClientEntity);
+ }
+
+ //Description may be null
+ if(aRecord.iDescription.Length() != 0)
+ {
+ //A long column is written by using an RDbColStream
+ RDbColWriteStream str;
+ TDbColNo col = colSet->ColNo(KColDescription);
+
+ str.OpenLC(aTable,col);
+ str.WriteL(aRecord.iDescription);
+ str.Close();
+
+ CleanupStack::PopAndDestroy(&str);
+ }
+
+ aTable.SetColL(colSet->ColNo(KColResult),aRecord.iResult);
+ aTable.SetColL(colSet->ColNo(KColEvaluatorInfo),(TUint32)aRecord.iEvaluatorInfo);
+ aTable.SetColL(colSet->ColNo(KColMajorPolicyVersion),aRecord.iMajorPolicyVersion);
+
+ CleanupStack::PopAndDestroy(colSet);
+ }
+
+
+EXPORT_C void CDecisionDbW::CreateDecisionL(CDecisionRecord& aRecord)
+/**
+ Inserts a new decision record into the decision database.
+
+ @param aRecord A new decision record
+ */
+ {
+ //Create a database table object
+ RDbTable dbTable;
+ User::LeaveIfError(dbTable.Open(iDatabase,KDecisionTable,dbTable.EInsertOnly));
+ CleanupClosePushL(dbTable);
+
+ //Set the rowset cursor to the beginning position
+ dbTable.Reset();
+
+ //Insert an empty row into the table
+ dbTable.InsertL();
+
+ //Set the fields of newly inserted row
+ UpdateCurrentRowL(dbTable, aRecord);
+
+ //Complete insertion
+ dbTable.PutL();
+
+ CleanupStack::Pop(&dbTable);
+ //Close the rowset
+ dbTable.Close();
+ }
+
+
+void CDecisionDbW::PrepareForSearchL(RDbTable& aTable, CDecisionFilter& aFilter, TDbSeekMultiKey<KIndexColumnNumber>& aSeekKey)
+/**
+ Opens the provided table object on the decision database and sets the decision index
+ as the active index for this table. If successful, the rowset is reset to the beginning.
+
+ @param aTable A table object which provides access to table data as a rowset.
+ @param aFilter A filter object which is used to set the decision index.
+ @return The key value to lookup in the index.
+ */
+ {
+ User::LeaveIfError(aTable.Open(iDatabase, KDecisionTable, aTable.EUpdatable));
+
+ TUint16 flags = KSetClientSid|KSetEvaluatorId|KSetServiceId|KSetServerSid|KSetFingerprint|KSetClientEntity|KSetMajorPolicyVersion;
+
+ TUint16 combinedFlags = (0x00FF) & (aFilter.iSetFlag[KPosClientSid] | aFilter.iSetFlag[KPosEvaluatorId] |
+ aFilter.iSetFlag[KPosServiceId] | aFilter.iSetFlag[KPosServerSid] |
+ aFilter.iSetFlag[KPosFingerprint] | aFilter.iSetFlag[KPosClientEntity]|
+ aFilter.iSetFlag[KPosMajorPolicyVersion]);
+
+ //If any of these flags is not set, do not continue
+ if ((combinedFlags & flags) != flags)
+ {
+ DEBUG_PRINTF(_L("The provided filter does not have all required keys to look up a decision."));
+ User::Leave(KErrUpsMissingArgument);
+ }
+
+ aSeekKey.Add((TUint)aFilter.iClientSid.iId);
+ aSeekKey.Add((TUint)aFilter.iEvaluatorId.iUid);
+ aSeekKey.Add((TUint)aFilter.iServiceId.iUid);
+ aSeekKey.Add((TUint)aFilter.iServerSid.iId);
+ aSeekKey.Add(*aFilter.iFingerprint);
+ aSeekKey.Add(*aFilter.iClientEntity);
+ aSeekKey.Add(aFilter.iMajorPolicyVersion);
+
+ //Set the primary index
+ User::LeaveIfError(aTable.SetIndex(KPrimaryIndex));
+
+ }
+
+
+EXPORT_C CDecisionRecord* CDecisionDbW::GetDecisionL(CDecisionFilter& aFilter)
+/**
+ Gets a decision record from the decision database. The functions returns the first matched
+ record. All the methods of the filter object except the client entity should be supplied to
+ get the intended decision record. If any other attribute of the filter object is missing,
+ either no decision might be found or a wrong decision may be retrieved
+
+ @param aFilter A filter object whose client id, policy evaluator id, service id,
+ server id, fingerprint (and client entity) attributes are set.
+ */
+ {
+ //Define a database table object handle
+ RDbTable dbTable;
+ CleanupClosePushL(dbTable);
+
+ //Open the database table object and prepares it for searching
+ TDbSeekMultiKey<KIndexColumnNumber> seekKey;
+ PrepareForSearchL(dbTable,aFilter,seekKey);
+
+ //Define the decision record that will be returned
+ CDecisionRecord* retRecord(0);
+
+ if(dbTable.SeekL(seekKey))
+ {
+ //Result found
+ CDbColSet* colSet = dbTable.ColSetL();
+ CleanupStack::PushL(colSet);
+
+ dbTable.GetL();
+
+ retRecord = CDecisionView::GenerateRecordL(dbTable,colSet);
+
+ CleanupStack::PopAndDestroy(colSet);
+ }
+
+ CleanupStack::PopAndDestroy(&dbTable);
+
+ return retRecord;
+ }
+
+
+EXPORT_C TBool CDecisionDbW::UpdateDecisionL(CDecisionFilter& aFilter, CDecisionRecord& aNewRecord)
+ {
+ //Define a database table object handle
+ RDbTable dbTable;
+ CleanupClosePushL(dbTable);
+
+ //Open the database table object and prepares it for searching
+ TDbSeekMultiKey<KIndexColumnNumber> seekKey;
+ PrepareForSearchL(dbTable,aFilter,seekKey);
+
+ TBool retValue = ETrue; //return value
+
+ if(dbTable.SeekL(seekKey))
+ {
+ //Get the current row
+ dbTable.GetL();
+ //Prepare it for update
+ dbTable.UpdateL();
+ //Set the new values
+ UpdateCurrentRowL(dbTable, aNewRecord);
+ //Commit the update
+ dbTable.PutL();
+ }
+ else
+ {
+ retValue = EFalse;
+ }
+
+ CleanupStack::PopAndDestroy(&dbTable);
+
+ return retValue;
+ }
+
+
+void CDecisionDbW::DoRemoveDecisionL(CDecisionFilter& aFilter)
+/**
+ Deletes a set of records from the decision database. This function is called
+ by RemoveDecisionsL in a loop to be able to catch and repair index corruptions.
+
+ @param aFilter A filter object
+ */
+ {
+ //Create the SQL statement
+ RBuf sqlStatement;
+ CreateSqlStatementLC(aFilter, sqlStatement);
+
+ RDbView dbView;
+ CleanupClosePushL(dbView);
+
+ User::LeaveIfError(dbView.Prepare(iDatabase,TDbQuery(sqlStatement),TDbWindow::EUnlimited));
+ User::LeaveIfError(dbView.EvaluateAll());
+
+ //Begin the delete transaction and lock the database
+ User::LeaveIfError(iDatabase.Begin());
+
+ while(dbView.NextL())
+ {
+ dbView.DeleteL();
+ }
+
+ CleanupStack::PopAndDestroy(&dbView);
+ CleanupStack::PopAndDestroy(&sqlStatement);
+
+ //Release the lock
+ TInt result = iDatabase.Commit();
+ if(KErrNone != result)
+ {
+ DEBUG_PRINTF2(_L("Removing a decision has returned with %d. Rollback is now in proggress."),result);
+ iDatabase.Rollback();
+ User::Leave(result);
+ }
+ }
+
+EXPORT_C void CDecisionDbW::RemoveDecisionsL(CDecisionFilter& aFilter)
+/**
+ Deletes a set of records from the decision database. It is possible to delete a specific
+ decision record or a set of records based on a given filter. While this function is in
+ progress, the database is locked not to lead to an inconsistent data retrieving. If there
+ is any index corruption in the decision able, it is tried to be repaired automatically.
+
+ @param aFilter A filter object
+ */
+ {
+ TInt loopCount=1;
+ TInt error;
+ do
+ {
+ ++loopCount;
+ TRAP(error,DoRemoveDecisionL(aFilter));
+ if(error == KErrCorrupt)
+ {
+ DEBUG_PRINTF2(_L("The database is corrupted and being recovered. Try %d"),loopCount);
+ error = iDatabase.Recover(); //Recover() may fail, so update error
+ }
+ }while(error==KErrCorrupt && loopCount<2);
+
+ User::LeaveIfError(error);
+ }
+
+
+EXPORT_C CDecisionDbCompactor *CDecisionDbW::PrepareCompactionLC()
+/**
+ Creates a database compaction object to perform asynchronous compaction on the database.
+
+ @return A pointer to the newly created database compaction object
+ The pointer is also put onto the cleanup stack.
+ */
+ {
+ DEBUG_PRINTF(_L("The Ups database is being compacted."));
+ CDecisionDbCompactor *compactor = CDecisionDbCompactor::NewLC();
+ User::LeaveIfError(compactor->iDbIncremental.Compact(iDatabase,compactor->iStep()));
+ return compactor;
+ }
+