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