authenticationservices/authenticationserver/source/server/authdb.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 24 Nov 2009 09:06:03 +0200
changeset 29 ece3df019add
permissions -rw-r--r--
Revision: 200948 Kit: 200948

/*
* Copyright (c) 2005-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: 
* CAuthDb Authentication server data store implementation
*
*/


/**
 @file 
*/

#include "authserver_impl.h"

using namespace AuthServer;

// The number of decimal digits needed to represent a 32 bit unsigned integer
const TInt KUint32Digits = 10;

// -------- DB helper functions --------


static void RollbackDb(TAny* aPtr)
/**
	Implements the TCleanupOperation prototype.  This
	function is used to rollback a database transaction
	if a leave occurs before it is committed.
	
	@param	aPtr			Pointer to RDbDatabase object
							to roll back.
	@see CleanupRollbackPushL
 */
	{
	RDbDatabase* db = static_cast<RDbDatabase*>(aPtr);
	if (db->InTransaction())
		db->Rollback();
	}


inline void CleanupRollbackPushL(RDbDatabase& aDb)
/**
	Puts the supplied database on the cleanup stack.
	The database is rolled back if a leave occurs.
	
	If a leave does not occur then the supplied database
	should be committed.  Do not call CleanupStack::PopAndDestroy
	to delete this object.
	
	@param	aDb				Database to roll back if
							a leave occurs.
 */
	{
	CleanupStack::PushL(TCleanupItem(RollbackDb, &aDb));
	}


static void StartTransactionLC(RDbDatabase& aDb)
/**
	Helper function starts a database transaction and
	puts a rollback cleanup item on the cleanup stack.
	
	@param	aDb				Database to start transaction on.
	@see CommitTransactionLD
 */
	{
	User::LeaveIfError(aDb.Begin());
	CleanupRollbackPushL(aDb);
	}


static void CommitTransactionLD(RDbDatabase& aDb)
/**
	Complements StartTransactionLC by removing the
	the rollback cleanup item from the cleanup stack
	and committing the database.
	
	@see StartTransactionLC
 */
	{
	CleanupStack::Pop(&aDb);
	User::LeaveIfError(aDb.Commit());
	}


// -------- factory functions --------


CAuthDb2* CAuthDb2::NewL(RFs& aFs)
/**
	Factory function allocates a new instance of CAuthDb.
	The database file is created if it does not already exist.
	
	@return					New instance of CAuthDb.
 */
	{
	CAuthDb2* self = CAuthDb2::NewLC(aFs);
	CleanupStack::Pop(self);
	return self;
	}


CAuthDb2* CAuthDb2::NewLC(RFs& aFs)
/**
	Factory function allocates new instance of CAuthDb2,
	putting it on the cleanup stack.  The database file
	is created if it does not already exist.
	
	@return					New instance of CAuthDb.
 */
	{
	CAuthDb2* self = new(ELeave) CAuthDb2;
	CleanupStack::PushL(self);
	self->ConstructL(aFs);
	return self;
	}


void CAuthDb2::ConstructL(RFs& aFs)
/**
	This second-phase constructor opens the
	database file, creating it if it does not
	already exist.
 */
	{
	TInt r;
	TFileName dbName(KDbName);
	dbName[0] = RFs::GetSystemDriveChar();

	r = OpenDbFileL(aFs, dbName);
	
	if (r == KErrNone)
		{
		if (! iAuthDbFile.IsDamaged() || iAuthDbFile.Recover() == KErrNone)
			return;
		}
	
	User::LeaveIfError(iAuthDbFile.Replace(aFs, dbName));
	iIsDbOpen = ETrue;
	StartTransactionLC(iAuthDbFile);
	
	// These commands are stored as narrow instead of wide
	// text to save c. 500 bytes in decompressed exe size.
	// The non-XIP EXE size is almost the same because DEFLATE
	// is used on the ELF file.
	
	static const TText8*const cmds[] =
		{
		_S8("CREATE TABLE Identities (idName COUNTER, IdentityId UNSIGNED INTEGER NOT NULL, Description VARCHAR(255) NOT NULL)"),
		_S8("CREATE UNIQUE INDEX idIndex ON Identities (idName)"),
		_S8("CREATE UNIQUE INDEX identityIdx ON Identities (IdentityId DESC)"),
		_S8("CREATE TABLE Prefs (PluginType UNSIGNED INTEGER NOT NULL, PluginId UNSIGNED INTEGER NOT NULL)"),
		_S8("CREATE UNIQUE INDEX typeIdx ON Prefs (PluginType ASC)"),
		_S8("CREATE TABLE TrainedPlugins (IdentityId UNSIGNED INTEGER NOT NULL, PluginId UNSIGNED INTEGER NOT NULL, TransientKey LONG VARBINARY NOT NULL)"),
		_S8("CREATE UNIQUE INDEX identityPluginIdx ON TrainedPlugins (IdentityId DESC, PluginId ASC)")
		};
	
	const TInt elemCount = sizeof(cmds) / sizeof(cmds[0]);
	TBuf<140> cmd;
	for (TInt i = 0; i < elemCount; ++i)
		{
		TPtrC8 cmd8(cmds[i]);
		cmd.Copy(cmd8);
		User::LeaveIfError(iAuthDbFile.Execute(cmd));
		}

	CommitTransactionLD(iAuthDbFile);
	}


CAuthDb2::~CAuthDb2()
/**
	Close the database file used by this object.
 */
	{
	CloseDbFile();
	}

TInt CAuthDb2::OpenDbFileL(RFs& aFs, const TFileName& aDbName)
	{
	
	TInt r(0);
	if(!iIsDbOpen)
		{
		r = iAuthDbFile.Open(aFs, aDbName);
		if(KErrNone == r)
			{
			iIsDbOpen = ETrue;
			}
		else if (KErrNotFound != r)
			{
			User::Leave(r);
			}
		}
	return r;
	}

void CAuthDb2::CloseDbFile()
	{
	if(iIsDbOpen)
		{
		RollbackDb(&iAuthDbFile);
		if(iAuthDbFile.IsDamaged())
			{
			iAuthDbFile.Recover();
			}
		iAuthDbFile.Close();
		iIsDbOpen = EFalse;
		}
	}

// -------- identities --------


void CAuthDb2::AddIdentityWithTrainedPluginL(
	TIdentityId aIdentityId, const TDesC& aDesc,
	const CTransientKeyInfo& aTransKeyInfo)
/**
	Adds an identity and trains a plugin for it in a single
	atomic operation.
	
	@param	aIdentityId		New identity.
	@param	aDesc			Identity's description.  This must
							have no more than KMaxDescLen characters.
	@param	aTransKeyInfo	A transient key which trains this identity
							for a plugin.
	@leave	KErrAuthServIdentityAlreadyExists  The supplied identity
							is already in the database.
 */
	{
	StartTransactionLC(iAuthDbFile);
	
	AddIdentityL(aIdentityId, aDesc);
	
	SetTrainedPluginL(aIdentityId, aTransKeyInfo.PluginId(), aTransKeyInfo);
	
	CommitTransactionLD(iAuthDbFile);
	}


void CAuthDb2::AddIdentityL(TIdentityId aIdentityId, const TDesC& aDesc)
/**
	Add the supplied identity to the database.
	
	@param	aIdentityId		New identity.
	@param	aDesc			Identity's description.  This must
							have no more than KMaxDescLen characters.
	@leave	KErrAuthServDescTooLong The supplied description is more than
							255 characters.
	@leave	KErrAuthServIdentityAlreadyExists  The supplied identity
							is already in the database.
 */
	{
	if (aDesc.Length() > KMaxDescLen)
		User::Leave(KErrAuthServDescTooLong);

	RDbTable table;
	
	// EUpdatable instead of EInsertOnly so can navigate to any
	// existing row.
	if (FindIdentityLC(table, RDbView::EUpdatable, aIdentityId))
		User::Leave(KErrAuthServIdentityAlreadyExists);
	
	table.InsertL();		// start automatic transaction
	table.SetColL(KIdentitiesIdentityIdCol, aIdentityId);
	table.SetColL(KIdentitiesDescCol, aDesc);
	table.PutL();			// end automatic transaction
	
	CleanupStack::PopAndDestroy(&table);
	}


void CAuthDb2::RemoveIdentityL(TIdentityId aIdentityId)
/**
	Remove the supplied identity from the auth server's database.
	Any trained entries for the supplied identity are also removed.
	All updates to the database happen within a single transaction.

	@param	aIdentityId		Identity to remove from database;
	@leave	KErrAuthServIdentityNotFound The supplied identity could
							not be found in the database.
 */
	{
	StartTransactionLC(iAuthDbFile);
	
	RDbTable table;
	FindExistingIdentityLC(table, RDbView::EUpdatable, aIdentityId);
	table.DeleteL();
	CleanupStack::PopAndDestroy(&table);

	// remove each training record for this identity
	_LIT(KDelTrainFmt, "DELETE FROM TrainedPlugins WHERE IdentityId=%u");
	TBuf<(46 - 2) + 10> bufCmd;
	bufCmd.Format(KDelTrainFmt, aIdentityId);
	iAuthDbFile.Execute(bufCmd);
	
	CommitTransactionLD(iAuthDbFile);
	}


TInt CAuthDb2::NumIdentitiesL() 
 /**
	Retrieve the number of identities in the database.
	@return the number of identities in the database.
  */
	{
	TInt result = 0;
	RDbTable table;
	OpenTableLC(table, KIdentitiesTableName, RDbRowSet::EReadOnly, KIdentitiesIndex);
	result = table.CountL();
	CleanupStack::PopAndDestroy(&table);
	return result;
	}
  
void CAuthDb2::IdentitiesL(RArray<TIdentityId>& aResults)
/**
	Populate the supplied array with the currently
	registered identities.
	
	@param	aResults		On success this array is populated
							with the currently registered identities.
							Any items which were in the array when
							this function is called are removed.  On
							failure this array is emptied so the caller
							does not have to place it on the cleanup stack.
 */
	{
	aResults.Reset();
	CleanupClosePushL(aResults);
	
	RDbTable table;
	OpenTableLC(table, KIdentitiesTableName, RDbRowSet::EReadOnly, KIdIndexNum);
	while (table.NextL())
		{
		table.GetL();
		aResults.AppendL(table.ColUint32(KIdentitiesIdentityIdCol));
		}
	
	CleanupStack::PopAndDestroy(&table);
	CleanupStack::Pop(&aResults);
	}

void CAuthDb2::IdentitiesWithDescriptionL(RIdAndStringArray& aList)
/**
	Populate the supplied array with the currently
	registered identities and descriptions.
	
	@param	aList   		On success this array is populated
							with the currently registered identities.
							Any items which were in the array when
							this function is called are removed.  On
							failure this array is emptied so the caller
							does not have to place it on the cleanup stack.
 */
	{
	aList.Reset();
	CleanupClosePushL(aList);
	
	RDbTable table;
	OpenTableLC(table, KIdentitiesTableName, RDbRowSet::EReadOnly, KIdentitiesIndex);
	while (table.NextL())
		{
		table.GetL();
		CIdAndString* id =
		  CIdAndString::NewLC(table.ColUint32(KIdentitiesIdentityIdCol),
							  table.ColDes(KIdentitiesDescCol));
		aList.AppendL(id);
		CleanupStack::Pop(id);
		}
	
	CleanupStack::PopAndDestroy(&table);
	CleanupStack::Pop(&aList);
	}

HBufC* CAuthDb2::DescriptionL(TIdentityId aIdentityId)
/**
	Allocate a descriptor containing the supplied identity's
	description.
	
	@param	aIdentityId		Identity whose description is returned.
	@return					Newly-allocated descriptor containing the
							identity's description.  The client must
							free this object.
	@leave	KErrAuthServIdentityNotFound The supplied identity could
							not be found in the database.
 */
	{
	RDbTable table;
	FindExistingIdentityLC(table, RDbView::EReadOnly, aIdentityId);
	
	table.GetL();
	HBufC* retDesc = table.ColDes(KIdentitiesDescCol).AllocL();
	
	CleanupStack::PopAndDestroy(&table);
	return retDesc;
	}



void CAuthDb2::SetDescriptionL(TIdentityId aIdentityId, const TDesC& aDesc)
/**
	Update an identity's description.
	
	@param	aIdentityId		Identity to update.
	@param	aDesc			New description text.  This must have
							no more than KMaxDescLen characters.
	@leave	KErrAuthServIdentityNotFound The supplied identity could
							not be found in the database.
 */
	{
	if (aDesc.Length() > KMaxDescLen)
		User::Leave(KErrAuthServDescTooLong);
	
	RDbTable table;
	FindExistingIdentityLC(table, RDbView::EUpdatable, aIdentityId);
	
	table.UpdateL();			// start automatic transaction
	table.SetColL(KIdentitiesDescCol, aDesc);
	table.PutL();				// end automatic transaction
	
	CleanupStack::PopAndDestroy(&table);
	}


void CAuthDb2::FindExistingIdentityLC(
	RDbTable& aTable, RDbRowSet::TAccess aAccess, TIdentityId aIdentityId)
/**
	Find the supplied identity and navigate to it.
	On success, the table handle is on the cleanup stack.
	
	@param	aTable			Caller's table handle.
	@param	aAccess			Mode in which to open the handle.
	@param	aIdentityId		Identity to navigate to.
	@leave	KErrAuthServIdentityNotFound The supplied identity could
							not be found.
 */
	{
	TBool found = FindIdentityLC(aTable, aAccess, aIdentityId);
	if (! found)
		User::Leave(KErrAuthServIdentityNotFound);
	}


TBool CAuthDb2::FindIdentityLC(RDbTable& aTable, RDbRowSet::TAccess aAccess, TIdentityId aIdentityId)
/**
	Navigate to the supplied identity in the Identities table.
	On success, the table handle is on the cleanup stack.
	
	@param	aTable			Caller's table handle.
	@param	aAccess			Mode in which to open the table.
	@param	aIdentityId		Identity to navigate to.
	@return					Zero if could not find identity in table;
							Non-zero otherwise.
 */
	{
	TBool found = OpenTableAndNavigateLC(
		aTable, KIdentitiesTableName, aAccess, KIdentitiesIndex, aIdentityId);

	return found;
	}


// -------- plugins --------

TInt CAuthDb2::NumTrainedPluginsL(TIdentityId aId)
/**
    Retrieve the number of trained plugins for the specified id.
    
    @param aId the identity for whom to retrieve the number of trained plugins.
    @return the number of trained plugins for the specified id.
 */
	{
	_LIT(KGetTrainedFmt, "SELECT IdentityId FROM TrainedPlugins WHERE IdentityId=%u");
	TBuf<(57 - 2) + 10> bufCmd;
	bufCmd.Format(KGetTrainedFmt, aId);
	
	TDbQuery q(bufCmd);
	RDbView viewTp;
	CleanupClosePushL(viewTp);
	User::LeaveIfError(viewTp.Prepare(iAuthDbFile, q));
	User::LeaveIfError(viewTp.EvaluateAll());
	TInt trainedCount = viewTp.CountL();
	CleanupStack::PopAndDestroy(&viewTp);

	return trainedCount;
	}

/**
	Returns list of plugins that are trained for the given identity

	@param	aIdentityId		The identity for whom to retrieve the list of trained plugins.
	@param 	aTrainedPlugins	The array to fill the returned plugin ids.
*/
void CAuthDb2::TrainedPluginsListL(TIdentityId aIdentityId, RArray<TPluginId>& aTrainedPlugins)
	{
	_LIT(KGetTrainedFmt, "SELECT PluginId FROM TrainedPlugins WHERE IdentityId=%u");
	RBuf queryBuf;
	CleanupClosePushL(queryBuf);
	queryBuf.CreateL(KGetTrainedFmt().Length() + KUint32Digits);
	queryBuf.Format(KGetTrainedFmt, aIdentityId);

	TDbQuery query(queryBuf);
	RDbView viewTp;
	CleanupClosePushL(viewTp);
	User::LeaveIfError(viewTp.Prepare(iAuthDbFile, query));
	User::LeaveIfError(viewTp.EvaluateAll());

	if (viewTp.FirstL())
		{
		do
			{
			viewTp.GetL();
			aTrainedPlugins.AppendL(viewTp.ColUint32(1));
			}
		while (viewTp.NextL());
		}
	CleanupStack::PopAndDestroy(2, &queryBuf); // viewTp
	}

TAuthTrainingStatus CAuthDb2::PluginStatusL(TPluginId aPluginId)
/**
	Return the supplied plugin's training status.

		EAuthUntrained			No identities have been trained for this plugin.
		EAuthTrained			Some (but not all) identities have been trained for this plugin
		EAuthFullyTrained		All identities have been trained for this plugin.
	
	@param	aPluginId		Plugin whose status should be retrieved.
	@return					The plugin's training status.
 */
	{
	// get number of identities trained for this plugin
	
	// This involves filtering all trainings for this plugin
	// from the TrainedPlugins table.  For small numbers of users
	// it is simpler to do this; for large numbers of users
	// (registered identities) it may be better to maintain another
	// table which matches each plugin against its training count.

	_LIT(KGetTrainedFmt, "SELECT IdentityId FROM TrainedPlugins WHERE PluginId=%u");
	TBuf<(55 - 2) + 10> bufCmd;
	bufCmd.Format(KGetTrainedFmt, aPluginId);
	
	TDbQuery q(bufCmd);
	RDbView viewTp;
	CleanupClosePushL(viewTp);
	User::LeaveIfError(viewTp.Prepare(iAuthDbFile, q));
	User::LeaveIfError(viewTp.EvaluateAll());
	TInt trainCount = viewTp.CountL();
	CleanupStack::PopAndDestroy(&viewTp);
	
	if (trainCount == 0)
		return EAuthUntrained;

	// get total number of identities in the database
	RDbTable tableId;
	OpenTableLC(tableId, KIdentitiesTableName, RDbRowSet::EReadOnly, KIdentitiesIndex);
	TInt idCount = tableId.CountL();
	CleanupStack::PopAndDestroy(&tableId);
	
	return (trainCount < idCount) ? EAuthTrained : EAuthFullyTrained;
	}


// -------- preferences --------


void CAuthDb2::SetPreferredPluginL(TAuthPluginType aPluginType, TPluginId aPluginId)
/**
	Set the preferred plugin for the supplied plugin type.

	If the type already has a preferred plugin type, its entry is
	updated.  Otherwise, a new entry is created.

	@param	aPluginType		Type of plugin to associate with a specific
							plugin ID.
	@param	aPluginId		Specific plugin to use for the plugin type.
 */
	{
	RDbTable table;

	// start automatic transaction
	if (FindPrefLC(table, RDbRowSet::EUpdatable, aPluginType))
		table.UpdateL();
	else
		table.InsertL();
	
	table.SetColL(KPrefsTypeCol, (TUint) aPluginType);
	table.SetColL(KPrefsIdCol, (TUint) aPluginId);
	table.PutL();			// end automatic transaction

	CleanupStack::PopAndDestroy(&table);
	}


void CAuthDb2::ClearPreferredPluginL(TAuthPluginType aPluginType)
/**
	Remove and preferred plugin ID for the supplied plugin type.
	
	If the supplied plugin type does not have a preferred plugin
	then the database is not modified.

	@param	aPluginType		Type of plugin to remove from the
							Prefs table.
 */
	{
	RDbTable table;
	if (FindPrefLC(table, RDbRowSet::EUpdatable, aPluginType))
		table.DeleteL();

	CleanupStack::PopAndDestroy(&table);
	}


TPluginId CAuthDb2::PreferredPluginL(TAuthPluginType aPluginType)
/**
	Retrieve the preferred plugin for the supplied plugin type.
	If the plugin type does not have a preferred plugin this function
	returns KUnknownPluginId.

	@param	aPluginType		Plugin type to find preferred plugin for.
	@return					Preferred plugin for aPluginType.  This is
							KUnknownPluginId if the type does not
							have a preferred plugin.
 */
	{
	TPluginId pluginId;

	RDbTable table;
	if (! FindPrefLC(table, RDbRowSet::EReadOnly, aPluginType))
		pluginId = KUnknownPluginId;
	else
		{
		table.GetL();
		pluginId = static_cast<TPluginId>(table.ColUint(KPrefsIdCol));
		}

	CleanupStack::PopAndDestroy(&table);
	return pluginId;
	}


TBool CAuthDb2::FindPrefLC(RDbTable& aTable, RDbRowSet::TAccess aAccess, TAuthPluginType aPluginType)
/**
	Open the Prefs table and navigate to the supplied plugin type.
	On success the table handle is placed on the cleanup stack.

	@param	aTable			Table handle to open.  On success this
							is placed on the cleanup stack.
	@param	aAccess			Mode in which the table should be opened.
	@param	aPluginType		Plugin type to navigate to.
	@return					Zero if could not find plugin type;
							Non-zero otherwise.
 */
	{
	return OpenTableAndNavigateLC(aTable, KPrefsTableName, aAccess, KPrefsTypeIndex, aPluginType);
	}


// -------- trained plugins --------


void CAuthDb2::SetTrainedPluginL(
	TIdentityId aIdentityId, TPluginId aPluginId, const CTransientKeyInfo& aTransKeyInfo)
/**
	Sets the transient key for the supplied identity, plugin pair.

	If the identity has already been trained for this plugin
	then the existing record is replaced.

	@param	aIdentityId		Identity to update.
	@param	aPluginId		Plugin to associate with identity.
	@param	aTransKeyInfo	The salt and encrypted protection key
							for this identity / plugin pair.
	@leave	KErrAuthServIdentityNotFound The supplied identity has
							not been registered.
 */
	{
	RDbTable table;

	TBool update = FindTrainedPluginLC(table, RDbRowSet::EUpdatable, aIdentityId, aPluginId);
	
	// start automatic transaction
	if (update)
		{
		table.UpdateL();
		}
	else
		{
		CheckIdentityRegisteredL(aIdentityId);
		table.InsertL();
		}

	table.SetColL(KTpIdentityIdCol, (TUint) aIdentityId);
	table.SetColL(KTpPluginIdCol, (TUint) aPluginId);
	
	RDbColWriteStream dbcws;
	dbcws.OpenLC(table, KTpTransientKeyCol);
	aTransKeyInfo.ExternalizeL(dbcws);
	dbcws.CommitL();
	CleanupStack::PopAndDestroy(&dbcws);
	table.PutL();
	
	CleanupStack::PopAndDestroy(&table);
	}


void CAuthDb2::RemoveTrainedPluginL(TIdentityId aIdentityId, TPluginId aPluginId)
/**
	Remove the transient key info (i.e. the encrypted protection key
	and its salt) for the supplied identity, plugin pair.

	@param	aIdentityId		Identity which should be trained for
							the supplied plugin.
	@param	aPluginId		Plugin which should be trained for the
							supplied identity.
	@leave	KErrAuthServTrainingNotFound Attempted to remove an
							identity, plugin pair which was not in the
							trained plugins table.
 */
	{
	RDbTable table;
	TBool found = FindTrainedPluginLC(table, RDbRowSet::EUpdatable, aIdentityId, aPluginId);

	if (! found)
		User::Leave(KErrAuthServTrainingNotFound);

	table.DeleteL();
	CleanupStack::PopAndDestroy(&table);
	}


CTransientKeyInfo* CAuthDb2::KeyInfoL(TIdentityId aIdentity, TPluginId aPluginId)
/**
	Retrieve the transient key associated with the supplied identity,
	plugin pair.

	@param	aIdentity		Identity to search for.
	@param	aPluginId		Plugin which should be registered
							with the supplied identity.
	@return					Transient key info, including encrypted
							protection key, associated with identity,
							plugin pair.
	@leave KErrAuthServTrainingNotFound There was no transient key
							information for the supplied identity,
							plugin pair.
 */
	{
	RDbTable table;
	TBool found = FindTrainedPluginLC(table, RDbRowSet::EReadOnly, aIdentity, aPluginId);
	if (! found)
		User::Leave(KErrAuthServTrainingNotFound);

	table.GetL();
	RDbColReadStream dbcrs;
	dbcrs.OpenLC(table, KTpTransientKeyCol);
	CTransientKeyInfo* keyInfo = CTransientKeyInfo::NewL(dbcrs);

	CleanupStack::PopAndDestroy(2, &table);		// dbcrs, table
	return keyInfo;
	}


TBool CAuthDb2::FindTrainedPluginLC(
	RDbTable& aTable, RDbRowSet::TAccess aAccess,
	TIdentityId aIdentityId, TPluginId aPluginId)
/**
	Helper function for SetTrainedPluginL and ClearTrainedPluginL.
	This function opens the TrainedPlugins table and navigates to
	the supplied identity, plugin pair if they are present.

	@param	aTable			Table handle to open.
	@param	aAccess			Mode in which to open the table.
	@param	aIdentityId		Identity to navigate to.
	@param	aPluginId		Plugin to navigate to.
	@return					Zero if could not navigate to the
							identity, plugin ID pair; non-zero
							otherwise.
 */
	{
	OpenTableLC(aTable, KTpTableName, aAccess, KTpIndex);

	TDbSeekMultiKey<2> dbsmk;
	dbsmk.Add((TUint) aIdentityId);
	dbsmk.Add((TUint) aPluginId);
	return aTable.SeekL(dbsmk);
	}


// -------- helper functions --------


TBool CAuthDb2::OpenTableAndNavigateLC(
	RDbTable& aTable, const TDesC& aTableName, RDbRowSet::TAccess aAccess,
	const TDesC& aIndexName, TUint aKey)
/**
	Open the described table and navigate to the identified row.

	@param	aTable			Caller's table handle.  On success
							this is placed on the cleanup stack.
	@param	aTableName		Table to open in auth db.
	@param	aAccess			Mode to open table in.
	@param	aIndexName		Index to use for navigating.
	@param	aKey			Key to find in database.
	@return					Zero if could not find row in table;
							Non-zero otherwise.
 */
	{
	OpenTableLC(aTable, aTableName, aAccess, aIndexName);

	TDbSeekKey seekKey((TUint)aKey);	// from TUint32
	return aTable.SeekL(seekKey);
	}


void CAuthDb2::OpenTableLC(
	RDbTable& aTable, const TDesC& aTableName,
	RDbRowSet::TAccess aAccess, const TDesC& aIndexName)
/**
	Open the named table in the supplied access mode,
	and use the index on it.

	@param	aTable			Caller's table handle.  On success
							this is placed on the cleanup stack.
	@param	aTableName		Table to open in the authentication db.
	@param	aAccess			Mode to open table in.
	@param	aIndexName		Index to activate on table.
 */
	{
	CleanupClosePushL(aTable);
	User::LeaveIfError(aTable.Open(iAuthDbFile, aTableName, aAccess));

	if (aAccess != RDbRowSet::EInsertOnly)
		{
		User::LeaveIfError(aTable.SetIndex(aIndexName));
		}
	}


void CAuthDb2::CheckIdentityRegisteredL(TUint aIdentityId)
/**
	Checks if the supplied identity has been registered with
	the database.  If not, it leaves with KErrAuthServIdentityNotFound.
	
	This function is used to validate arguments to other functions.
	
	@param	aIdentityId		Identity to search for.
	@leave	KErrAuthServIdentityNotFound The supplied identity has not
							been registered.
 */
	{
	CheckItemRegisteredL(
		KIdentitiesTableName, KIdentitiesIndex,
		aIdentityId, KErrAuthServIdentityNotFound);
	}


void CAuthDb2::CheckItemRegisteredL(
	const TDesC& aTableName, const TDesC& aIndexName,
	TUint aValue, TInt aNotFoundError)
/**
	Helper function for CheckIdentityRegisteredL.
	
	@param	aTableName		Name of table to search.
	@param	aIndexName		Name of index to use on table.
	@param	aValue			Key value to search for.
	@param	aNotFoundError	If the key value cannot be found
		`					then leave with this value.
 */
 	{
	RDbTable table;
	if (! OpenTableAndNavigateLC(table, aTableName, RDbRowSet::EReadOnly, aIndexName, aValue))
		User::Leave(aNotFoundError);
	CleanupStack::PopAndDestroy(&table);
 	}


// -------- compaction --------


const TInt KBlockSize = 512;
const TInt KBlockMask = KBlockSize - 1;


void CAuthDb2::CompactIfRequired()
/**
	Compacts the database file if compacting it would
	recover at least one 512 byte block.
	
	This function does not report any error status
	but, if the database is damaged, ConstructL will
	attempt to repair it.
 */
	{
	TInt r = iAuthDbFile.UpdateStats();
	
	if (r == KErrNone)
		{
		RDbDatabase::TSize sz = iAuthDbFile.Size();
		
		// approx used bytes - sz.iUsage is a percentage
		TInt usedBytes = sz.iUsage * 100;
		if ((usedBytes & ~KBlockMask) < (sz.iSize & ~KBlockMask))
			/* ignore */ iAuthDbFile.Compact();
		}
	}

TIdentityId CAuthDb2::DefaultIdentityL()
/**
 	Return the default identity which is the first identity
 	in the table when indexed based on the order of insertion.
*/
	{
	TIdentityId identity(0);
	RDbTable table;	
	OpenTableLC(table, KIdentitiesTableName, RDbRowSet::EReadOnly, KIdIndexNum);
	
	//Position the cursor on the first row and retrieve the Identity.
	if (table.FirstL())
		{
		table.GetL();
		identity = table.ColUint32(KIdentitiesIdentityIdCol);
		}	
		
	CleanupStack::PopAndDestroy(&table);
	return identity;
	}