pimprotocols/phonebooksync/Server/SyncEngineServer.cpp
changeset 0 e686773b3f54
child 81 640d30f4fb64
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pimprotocols/phonebooksync/Server/SyncEngineServer.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,2143 @@
+// Copyright (c) 2002-2009 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:
+// Implementation of the Background Sync Engine server. The engine provides
+// the ability to perform Phonebook Sync's long running operations (e.g. Sync,
+// write contact and delete contact).
+// 
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <connect/sbdefs.h>
+#include <commsdattypesv1_1.h>
+#include <cdblen.h>
+
+#include "common.h"
+#include "PhonebookManager.h"
+#include "SyncContactICCEntry.h"
+#include "phbksynclog.h"
+#include "SyncEngineServer.h"
+#include "SyncEngineSession.h"
+#include "SyncContactsWithICC.h"
+#include "WriteContactToICC.h"
+#include "DeleteContactFromICC.h"
+
+
+using namespace CommsDat;
+
+
+/**
+ * Number of retries for connecting to Etel.
+ */
+const TInt KMaxConnectToEtelRetries = 3;
+
+
+//
+// Teleplate names.  Should be in a resource file for localisation really!
+//
+_LIT(KPhbkTemplateADN, "SIM Card Contacts ADN");
+_LIT(KPhbkTemplateSDN, "SIM Card Contacts SDN");
+_LIT(KPhbkTemplateLND, "SIM Card Contacts LND");
+_LIT(KPhbkTemplateUSimApp, "SIM Card Contacts USIMAPP");
+_LIT(KPhbkTemplateFDN, "SIM Card Contacts FDN");
+_LIT(KPhbkTemplateNotSpecified, "SIM Card Contacts Name Not Specified");
+
+
+//
+// Constants for encoding contacts...
+//
+_LIT(KEngineInternationalPrefix,"+");
+
+
+//
+//  Definition of iPolicy dictating the capability checking for phbksyncsvr...
+//
+const TUint iEngineRangeCount = 10;
+	
+const TInt CSyncEngineServer::iRanges[iEngineRangeCount] = 
+	{
+	0,		//range is 0
+	1,		//range is 1-3 inclusive
+	4,		//range is 4-5 inclusive
+	6,		//range is 6
+	7,		//range is 7-13 inclusive
+	14,		//range is 14
+	15,		//range is 15-16 inclusive
+	17,		//range is 17-24 inclusive
+	25,		//range is 25 inclusive
+	26,		//range is 26-KMaxTInt inclusive
+	};
+
+const TUint8 CSyncEngineServer::iElementsIndex[iEngineRangeCount] = 
+	{
+	0,
+	1,
+	2,
+	3,
+	4,
+	5,
+	6,
+	7,
+	8,
+	CPolicyServer::ENotSupported,
+	};
+
+const CPolicyServer::TPolicyElement CSyncEngineServer::iElements[] = 
+	{
+	{ _INIT_SECURITY_POLICY_C2( ECapabilityReadUserData, ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 0:  range 0 - 0
+	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient},	// policy 1:  range 1 - 3
+	{ _INIT_SECURITY_POLICY_C1( ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 2:  range 4 - 5
+	{ _INIT_SECURITY_POLICY_C1( ECapabilityReadUserData), CPolicyServer::EFailClient},	// policy 3:  range 6 - 6
+	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient},	// policy 4:  range 7 - 13
+	{ _INIT_SECURITY_POLICY_C2( ECapabilityReadUserData, ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 5:  range 14 - 14
+	{ _INIT_SECURITY_POLICY_C1( ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 6:  range 15 - 16
+	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient},	// policy 7:  range 17 - 24
+#ifdef _DEBUG
+	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient}, // policy 8: range 25
+#else
+	{ _INIT_SECURITY_POLICY_FAIL}, // policy 8: range 25
+#endif
+	};
+
+const CPolicyServer::TPolicy CSyncEngineServer::iPolicy = 
+	{
+	CPolicyServer::EAlwaysPass ,
+	iEngineRangeCount,
+	iRanges,
+	iElementsIndex,
+	iElements
+	};
+
+
+/**
+ *  Static factory method used to create a CSyncEngineServer object.
+ *
+ *  @param aPhoneBookManager  Reference to the front-end servers's phonebook
+ *                            manager.
+ *
+ *  @return  Pointer to the created CSyncEngineServer object, or NULL.
+ */
+CSyncEngineServer* CSyncEngineServer::NewL(CPhoneBookManager& aPhoneBookManager)
+	{
+	LOGENGINE1(_L8("NewL()"));
+
+	CSyncEngineServer*  self = new (ELeave) CSyncEngineServer(aPhoneBookManager);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+
+	return self;
+	} // CSyncEngineServer::NewL
+
+
+/**
+ *  Standard constructor.
+ *
+ *  @param aPhoneBookManager  Reference to the front-end servers's phonebook
+ *                            manager.
+ */
+CSyncEngineServer::CSyncEngineServer(CPhoneBookManager& aPhoneBookManager)
+	: CPolicyServer(EPriorityNormal, iPolicy, ESharableSessions), 
+	iICCCaps(RMobilePhone::KCapsSimAccessSupported), 
+	iPhoneBookSyncEngineStarter(NULL),
+	iConnectedSession(NULL),
+	iPhonebookManager(aPhoneBookManager),
+	iSyncContactsWithICC(NULL),
+	iWriteContactToICC(NULL),
+	iDeleteContactFromICC(NULL)
+	{
+	__DECLARE_NAME(_L("CSyncEngineServer"));
+	} // CSyncEngineServer::CSyncEngineServer
+
+
+/**
+ *  Second phase constructor. Ensures the server is created and ready to run.
+ */
+void CSyncEngineServer::ConstructL()
+	{
+	StartL(PHBKSYNC_ENGINE_NAME);
+	} // CSyncEngineServer::ConstructL
+
+
+/**
+ *  Destructor.
+ */
+CSyncEngineServer::~CSyncEngineServer()
+	{
+	//
+	// Stop the engine from starting if it has initiated a startup.
+	//
+	delete iPhoneBookSyncEngineStarter;
+	iPhoneBookSyncEngineStarter = NULL;
+	
+	//
+	// Unconfigure the engine if needed...
+	//
+	TRAP_IGNORE(UnconfigureEngineL());
+
+	delete iSyncContactsWithICC;
+	delete iWriteContactToICC;
+	delete iDeleteContactFromICC;
+	} // CSyncEngineServer::~CSyncEngineServer
+
+
+/**
+ *  Configures the engine for use. This includes opening the ETel handles,
+ *  connecting to the contacts database and opening the phonebook stores.
+ */
+void CSyncEngineServer::ConfigureEngineL()
+	{
+	LOGENGINE1(_L8("CSyncEngineServer::ConfigureEngineL()"));
+
+	//
+	// Connect to ETel...
+	//
+	TInt  etelErr(KErrNone), retryCount;
+
+	for (retryCount = 0;  retryCount < KMaxConnectToEtelRetries;  retryCount++)
+		{
+		TRAP(etelErr, ConnectToEtelL());	//This can leave due to a denied access 										
+											//to CommDb or Etel
+		if (etelErr == KErrNone)
+			{
+			break;
+			}
+
+		if (retryCount < KMaxConnectToEtelRetries - 1)
+			{
+			User::After(1000000);
+			}
+		}
+	User::LeaveIfError(etelErr);
+
+	//
+	// Open the Contacts DB...
+	//
+	TInt  dbErr(KErrNone);
+
+	for (retryCount = 0;  retryCount < KMaxDbAccessRetryCount;  retryCount++)
+		{
+		TRAP(dbErr, iDb = CContactDatabase::OpenL());	// First try to open existing database
+		
+		if (dbErr == KErrNotFound)
+			{
+			TRAP(dbErr, iDb = CContactDatabase::CreateL()); // Database does not exist so create and open new one
+			}
+		
+		if (dbErr == KErrNone)
+			{
+			break;
+			}
+
+		if (retryCount < KMaxDbAccessRetryCount - 1)
+			{
+			User::After(1000000);
+			}
+		}
+	User::LeaveIfError(dbErr);
+
+	//
+	// Setup the phonebook parameters, read the settings from the INI
+	// file and create the phonebook group IDs...
+	//
+	if (iPhone.GetIccAccessCaps(iICCCaps) != KErrNone) // Get type of ICC that is being used 
+		{
+		iICCCaps = RMobilePhone::KCapsSimAccessSupported; // set default value
+		}
+
+	CreatePhoneBookIdsL();
+	} // CSyncEngineServer::ConfigureEngineL
+
+
+/**
+ *  Unconfigures the engine ready to shutdown.
+ */
+void CSyncEngineServer::UnconfigureEngineL()
+	{
+	LOGENGINE1(_L8("CSyncEngineServer::UnconfigureEngineL()"));
+
+	iPhonebookManager.ClosePhoneBookStores();
+
+	delete iDb;
+	iDb = NULL;
+
+	iPhone.Close();
+	iEtelServer.Close();
+	} // CSyncEngineServer::UnconfigureEngineL
+
+
+/**
+ *  Create a Template ID for the specified phonebook.
+ *
+ *  @param aPhonebookUid  UID of the phonebook to create an ID for.
+ *  @param aICCCaps       The capabilities of the phone.
+ */
+void CSyncEngineServer::CreateTemplateIdL(TUid aPhonebookUid,
+										  TUint32 aICCCaps)
+	{
+	LOGENGINE2(_L8("CreateTemplateIdL(0x%08x)"), aPhonebookUid);
+
+	TContactItemId  templateId(KNullContactId);
+	
+	//
+	// Check if there is an existing template in the contact database for
+	// this phonebook...
+	//
+	CContactIdArray*  idList = iDb->GetCardTemplateIdListL();
+	CleanupStack::PushL(idList);	
+	
+	if (idList != NULL  &&  idList->Count() > 0)
+		{
+		TInt  idCount = idList->Count();
+
+		for (TInt index = 0;  index < idCount;  index++)
+			{
+			CContactCardTemplate* item = static_cast<CContactCardTemplate*> (iDb->ReadContactLC((*idList)[index]));
+
+			if ((aPhonebookUid == KUidIccGlobalAdnPhonebook  && 
+				 item->GetTemplateLabelL() == KPhbkTemplateADN)  ||
+				(aPhonebookUid == KUidIccGlobalSdnPhonebook  && 
+				 item->GetTemplateLabelL() == KPhbkTemplateSDN)  ||
+				(aPhonebookUid == KUidIccGlobalLndPhonebook  && 
+				 item->GetTemplateLabelL() == KPhbkTemplateLND)  ||
+				(aPhonebookUid == KUidUsimAppAdnPhonebook  && 
+				 item->GetTemplateLabelL() == KPhbkTemplateUSimApp)  ||
+				(aPhonebookUid == KUidIccGlobalFdnPhonebook  && 
+				 item->GetTemplateLabelL() == KPhbkTemplateFDN))
+				{
+				templateId = (*idList)[index];
+
+				User::LeaveIfError(iPhonebookManager.SetTemplateId(aPhonebookUid,
+																   templateId));
+
+				index = idCount; // to terminate loop
+				}
+			CleanupStack::PopAndDestroy(item);
+			}
+		}
+	CleanupStack::PopAndDestroy(idList);
+
+	//
+	// If no suitable template been found create a new one...
+	//
+	if (templateId == KNullContactId)
+		{
+		LOGENGINE2(_L8("CreateTemplateIdL(0x%08x): Creating template."),
+				   aPhonebookUid);
+
+		CContactItem*  newTemplate;
+
+		if (aPhonebookUid == KUidIccGlobalAdnPhonebook)
+			{
+			newTemplate = iDb->CreateContactCardTemplateLC(KPhbkTemplateADN);
+			}
+		else if (aPhonebookUid == KUidIccGlobalSdnPhonebook)
+			{
+			newTemplate = iDb->CreateContactCardTemplateLC(KPhbkTemplateSDN);
+			}
+		else if (aPhonebookUid == KUidIccGlobalLndPhonebook)
+			{
+			newTemplate = iDb->CreateContactCardTemplateLC(KPhbkTemplateLND);
+			}
+		else if (aPhonebookUid == KUidUsimAppAdnPhonebook)
+			{
+			newTemplate = iDb->CreateContactCardTemplateLC(KPhbkTemplateUSimApp);
+			}
+		else if (aPhonebookUid == KUidIccGlobalFdnPhonebook)
+			{
+			newTemplate = iDb->CreateContactCardTemplateLC(KPhbkTemplateFDN);
+			}
+		else
+			{
+			newTemplate = iDb->CreateContactCardTemplateLC(KPhbkTemplateNotSpecified);
+			}
+
+		templateId = newTemplate->Id();
+		CleanupStack::PopAndDestroy(newTemplate);
+
+		//
+		// Remove all the unnecessary fields...
+		//
+		newTemplate = iDb->OpenContactLX(templateId);
+		CleanupStack::PushL(newTemplate);	
+		const TInt fieldCount = newTemplate->CardFields().Count();
+		for(TInt i=fieldCount-1;i>=0;i--)
+			{
+			newTemplate->RemoveField(i);
+			}
+
+		//
+		// Add default name field...
+		//
+		CContactItemField* name = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldFamilyName);
+		name->SetMapping(KUidContactFieldVCardMapUnusedN);
+		newTemplate->AddFieldL(*name);
+		CleanupStack::Pop(name);
+			
+		//
+		// Add second name field...
+		//
+		CContactItemField* secondName = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldSecondName);
+		secondName->SetMapping(KUidContactFieldVCardMapSECONDNAME);
+		secondName->SetUserFlags(EContactCategoryHome);
+		newTemplate->AddFieldL(*secondName);
+		CleanupStack::Pop(secondName);
+			
+		//
+		// Add default number field...
+		//
+		CContactItemField* number = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldPhoneNumber);
+		number->SetMapping(KUidContactFieldVCardMapTEL);
+		number->AddFieldTypeL(KUidContactFieldVCardMapWORK);
+		number->AddFieldTypeL(KUidContactFieldVCardMapVOICE);
+		number->AddFieldTypeL(KUidContactFieldVCardMapCELL);
+		newTemplate->AddFieldL(*number);
+		CleanupStack::Pop(number);
+
+		//
+		// Add Slot Number field...
+		//
+		CContactItemField* slotnum = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldICCSlot);
+		slotnum->SetMapping(KUidContactFieldVCardMapNotRequired);
+		newTemplate->AddFieldL(*slotnum);
+		CleanupStack::Pop(slotnum);
+
+		//
+		// Add Phonebook type field...
+		//
+		CContactItemField* phonebook = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldICCPhonebook);
+		phonebook->SetMapping(KUidContactFieldVCardMapNotRequired);
+		newTemplate->AddFieldL(*phonebook);
+		CleanupStack::Pop(phonebook);
+
+		//
+		// 3G ICC so there are additional fields for ADN and USIM App
+		// phonebooks.
+		//
+		if (aICCCaps & RMobilePhone::KCapsUSimAccessSupported)
+			{
+			if (aPhonebookUid == KUidIccGlobalAdnPhonebook  || 
+			    aPhonebookUid == KUidUsimAppAdnPhonebook)
+				{
+				//
+				// Add e-mail field...
+				//
+				CContactItemField* emailField = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldEMail);
+				emailField->SetMapping(KUidContactFieldVCardMapUnusedN);
+				newTemplate->AddFieldL(*emailField);
+				CleanupStack::Pop(emailField);
+
+				//
+				// Add group field - this is different from contacts group.
+				// This field indicates group that this ICC entry belongs to.
+				// User can add this entry to a number of groups on ICC i.e.
+				// business, private, etc. 
+				//
+				CContactItemField* group = CContactItemField::NewLC(KStorageTypeText,KUidContactFieldICCGroup);
+				group->SetMapping(KUidContactFieldVCardMapUnusedN);
+				newTemplate->AddFieldL(*group);
+				CleanupStack::Pop(group);
+				}
+			}
+
+		//
+		// Store the new Template ID...
+		//
+		User::LeaveIfError(iPhonebookManager.SetTemplateId(aPhonebookUid, templateId));
+
+		iDb->CommitContactL(*newTemplate);
+		CleanupStack::PopAndDestroy(2); // newTemplate plus locked record
+		}
+	} // CSyncEngineServer::CreateTemplateIdL
+
+		
+/**
+ *  Create a Group ID for the specified phonebook.
+ *
+ *  @param aPhonebookUid  UID of the phonebook to create an ID for.
+ */
+void CSyncEngineServer::CreateGroupIdL(TUid aPhonebookUid)
+	{
+	LOGENGINE2(_L8("CreateGroupIdL(0x%08x)"), aPhonebookUid);
+
+	TContactItemId  groupId(KNullContactId);
+
+	//
+	// Check if there is an existing group in the contact database for this
+	// phonebook...
+	//
+	CContactIdArray*  idList = iDb->GetGroupIdListL();
+	CleanupStack::PushL(idList);
+
+	if (idList != NULL  &&  idList->Count() > 0)
+		{
+		LOGENGINE2(_L8("CreateGroupIdL(0x%08x): Creating template."),
+				   aPhonebookUid);
+
+		TInt  idCount = idList->Count();
+
+		for (TInt index = 0;  index < idCount;  index++)
+			{
+			//
+			// Retrieve the first entry in each group, obtain that entry's
+			// template ID and check the template label to confirm if it
+			// relates to this phonebook.
+			//
+			CContactGroup*  group = static_cast<CContactGroup*> (iDb->ReadContactLC((*idList)[index]));
+			CContactIdArray*  itemList = group->ItemsContainedLC();
+
+			if (itemList->Count() > 0)
+				{
+				CContactItem*  groupItem = iDb->ReadContactLC((*itemList)[0]);
+				TContactItemId templateId = groupItem->TemplateRefId();
+				if (templateId != KNullContactId)
+					{
+					CContactCardTemplate*  tempItem = static_cast<CContactCardTemplate*> (iDb->ReadContactLC(templateId));
+					
+					TPtrC templateLabel;
+					TRAPD(errorCheck, templateLabel.Set(tempItem->GetTemplateLabelL()));
+					
+					// Check if the first item in the group uses a template with a label then check to see if the
+					// label matches one of the Phonebook template labels
+					if(errorCheck != KErrNotFound)
+						{
+						if(	(aPhonebookUid == KUidIccGlobalAdnPhonebook && templateLabel == KPhbkTemplateADN) ||
+				   			(aPhonebookUid == KUidIccGlobalSdnPhonebook && templateLabel == KPhbkTemplateSDN) ||
+						   	(aPhonebookUid == KUidIccGlobalLndPhonebook && templateLabel == KPhbkTemplateLND) ||
+						   	(aPhonebookUid == KUidIccGlobalFdnPhonebook && templateLabel == KPhbkTemplateFDN) ||
+							(aPhonebookUid == KUidUsimAppAdnPhonebook   && templateLabel == KPhbkTemplateUSimApp))
+							{
+							groupId = (*idList)[index];
+							User::LeaveIfError(iPhonebookManager.SetGroupId(aPhonebookUid,groupId));
+							index = idCount; // to terminate loop
+							}
+						}
+					CleanupStack::PopAndDestroy(tempItem);	
+					}
+				CleanupStack::PopAndDestroy(groupItem);
+				}
+			CleanupStack::PopAndDestroy(2, group); // itemList, group
+			}
+		}
+
+	//
+	// If no suitable group has been found create a new one...
+	//
+	if (groupId == KNullContactId)
+		{
+		//
+		// Store the new group ID...
+		//
+ 		CContactItem*  group = iDb->CreateContactGroupLC(KNullDesC);
+ 		groupId = group->Id();
+
+		User::LeaveIfError(iPhonebookManager.SetGroupId(aPhonebookUid, groupId));
+ 		CleanupStack::PopAndDestroy(group);
+		}
+
+	CleanupStack::PopAndDestroy(idList);
+	} // CSyncEngineServer::CreateGroupIdL
+
+
+/**
+ *  Create the template ID and group ID for all supported phonebooks. These
+ *  IDs are to be used with the entries that reside on a particular phonebook.
+ */
+void CSyncEngineServer::CreatePhoneBookIdsL()
+	{
+	LOGENGINE1(_L8("CreatePhoneBookIdsL()"));
+
+	//
+	// Ensure each phonebook has its templates created...
+	//
+	TInt  phonebookUidCount(iPhonebookManager.GetPhonebookCount());
+	
+	for (TInt index = 0;  index < phonebookUidCount;  index++)
+		{
+		TUid  phonebookUid;
+		
+		User::LeaveIfError(iPhonebookManager.GetPhonebookUid(index,
+															 phonebookUid));
+
+		LOGENGINE2(_L8("CreatePhoneBookIdsL: Phonebook=0x%08x"), phonebookUid);
+
+		//
+		// Get the template ID...
+		//
+		TContactItemId  templateId;
+		
+		User::LeaveIfError(iPhonebookManager.GetTemplateId(phonebookUid,
+														   templateId));
+
+		//
+		// Validate any template ID already provided in INI file
+		//
+		if (templateId != KNullContactId)
+			{
+			CContactItem*  item(NULL);
+			
+			TRAPD(err, item = iDb->ReadContactL(templateId));
+			CleanupStack::PushL(item);
+			
+			if (err == KErrNotFound)
+				{
+ 				User::LeaveIfError(iPhonebookManager.SetTemplateId(phonebookUid,
+ 																   KNullContactId));
+				templateId = KNullContactId;
+				}
+			else if (err != KErrNone)
+				{
+				User::Leave(err);
+				}
+			else if (item == NULL  ||  item->Type() != KUidContactCardTemplate)
+				{
+ 				User::LeaveIfError(iPhonebookManager.SetTemplateId(phonebookUid,
+ 																   KNullContactId));
+				templateId = KNullContactId;
+				}
+			else
+				{
+				TPtrC  label = static_cast<CContactCardTemplate*>(item)->GetTemplateLabelL();
+				
+				//
+				// The following test confirms if a template ID relates to this
+				// phonebook's template by checking the label employed.
+				//
+				if (!((phonebookUid == KUidIccGlobalAdnPhonebook  &&  label == KPhbkTemplateADN)  ||
+					  (phonebookUid == KUidIccGlobalSdnPhonebook  &&  label == KPhbkTemplateSDN)  ||
+					  (phonebookUid == KUidIccGlobalLndPhonebook  &&  label == KPhbkTemplateLND)  ||
+					  (phonebookUid == KUidUsimAppAdnPhonebook  &&  label == KPhbkTemplateUSimApp)  ||
+					  (phonebookUid == KUidIccGlobalFdnPhonebook  &&  label == KPhbkTemplateFDN)))
+					{
+	 				User::LeaveIfError(iPhonebookManager.SetTemplateId(phonebookUid,
+ 																	   KNullContactId));
+					templateId = KNullContactId;
+					}
+				}
+
+			CleanupStack::PopAndDestroy(item);
+			}
+
+		//
+		// If we don't have a template ID, then create a template...
+		//
+		if (templateId == KNullContactId)
+			{
+			CreateTemplateIdL(phonebookUid, iICCCaps);
+			}
+
+		//
+		// Get the group ID...
+		//
+		TContactItemId  groupId;
+		
+		User::LeaveIfError(iPhonebookManager.GetGroupId(phonebookUid, groupId));
+
+		//
+		// Validate any group ID already provided in INI file
+		//
+		if (groupId != KNullContactId)
+			{
+			CContactItem*  item = NULL;
+			TRAPD(err, item = iDb->ReadContactL(groupId));
+			CleanupStack::PushL(item);
+
+			if (err == KErrNotFound)
+				{
+				User::LeaveIfError(iPhonebookManager.SetGroupId(phonebookUid,
+																KNullContactId));
+				groupId = KNullContactId;
+				}
+			else if (err != KErrNone)
+				{
+				User::Leave(err);
+				}
+			else if (item == NULL  ||  item->Type() != KUidContactGroup)
+				{
+				User::LeaveIfError(iPhonebookManager.SetGroupId(phonebookUid,
+																KNullContactId));
+				groupId = KNullContactId;
+				}
+			else
+				{
+				//
+				// The following test is a simplistic method to determine
+				// if the group ID represents a phonebook group - as
+				// employed by LPD for Linnea. It does not attempt to check
+				// if it relates to the correct type of phonebook.
+				//
+				if (static_cast<CContactGroup*>(item)->GetGroupLabelL() != KNullDesC)
+					{
+					User::LeaveIfError(iPhonebookManager.SetGroupId(phonebookUid,
+																	KNullContactId));
+					groupId = KNullContactId;
+					}
+				}
+			CleanupStack::PopAndDestroy(item);
+			}
+
+		if (groupId == KNullContactId)
+			{
+			CreateGroupIdL(phonebookUid);
+			}
+		}
+	} // CSyncEngineServer::CreatePhoneBookIdsL
+
+
+/**
+ *  Removes all the contact entries for a phonebook. This is called when a
+ *  sync request is made on an unsupported phonebook so that it can be
+ *  cleared.
+ *
+ *  @param aPhonebookUid  UID of the ICC phonebook to perform the operation on.
+ */
+void CSyncEngineServer::RemoveAllContactsForPhoneBookL(TUid aPhonebookUid)
+	{
+	LOGENGINE2(_L8("RemoveAllContactsForPhoneBookL(0x%08x)"), aPhonebookUid);
+
+	//
+	// Get the Group ID to search for entries. If no ID exists then the
+	// database does not contain any entries.
+	//
+	TContactItemId  groupId;
+	
+	User::LeaveIfError(iPhonebookManager.GetGroupId(aPhonebookUid, groupId));
+
+ 	//
+ 	// Delete all entries for this group ID...
+ 	//
+ 	if (groupId != KNullContactId  &&  groupId != KGoldenTemplateId)
+		{
+ 		CContactItem*  group = iDb->ReadContactLC(groupId);
+ 		const CContactIdArray*  members = static_cast<CContactGroup*>(group)->ItemsContained();
+ 		const TInt  count = members->Count();
+
+		for (TInt item = 0;  item < count;  item++)
+			{
+			iDb->DeleteContactL((*members)[item]);
+			}
+
+		CleanupStack::PopAndDestroy(group);
+
+		//
+		// Compress the database if needed and we are finished!
+		//
+		if (iDb->CompressRequired())
+			{
+			TRAP_IGNORE(iDb->CompactL());
+			}
+		}
+	} // CSyncEngineServer::RemoveAllContactsForPhoneBookL
+
+
+/**
+ *  This method synchronises the specified phonebook. The synchronisation is
+ *  done by creating an instance of the CSyncContactsWithICC Active Object 
+ *  class and then starting it.
+ *
+ *  @param aMessage       A reference to the front-end server request.
+ *  @param aPhonebookUid  UID of the ICC phonebook to perform the operation on.
+ */
+void CSyncEngineServer::DoSynchronisationL(const RMessage2& aMessage,
+										   TUid aPhonebookUid)
+	{
+	LOGENGINE2(_L8("DoSynchronisationL(0x%08x)"), aPhonebookUid);
+
+	//
+	// Set the cache state to unsynchronised to start with.
+	//
+	TInt  result;
+
+	result = iPhonebookManager.SetSyncState(aPhonebookUid,
+											RPhoneBookSession::EUnsynchronised);
+	if (result != KErrNone)
+		{
+		iConnectedSession->CompleteRequest(aMessage, result);
+		return;
+		}
+
+	//
+	// Has the PhBkInfo structure been received yet? If not then this
+	// phonebook is not usable...
+	//
+	TInt  phBkInfoRetrievedResult;
+	
+	result = iPhonebookManager.GetPhBkInfoRetrievedResult(aPhonebookUid,
+														  phBkInfoRetrievedResult);
+	if (result != KErrNone)
+		{
+		iPhonebookManager.SetLastSyncError(aPhonebookUid, result);
+   		iPhonebookManager.SetSyncState(aPhonebookUid, RPhoneBookSession::EErrorDuringSync);
+
+		iConnectedSession->CompleteRequest(aMessage, result);
+		return;
+		}
+	
+	if (phBkInfoRetrievedResult != KErrNone)
+		{
+		//		
+		// The phonebook has been found to not exist, and a sync has been
+		// requested, so remove all entries from the database.
+		//
+		RemoveAllContactsForPhoneBookL(aPhonebookUid);
+
+		iPhonebookManager.SetLastSyncError(aPhonebookUid, phBkInfoRetrievedResult);
+   		iPhonebookManager.SetSyncState(aPhonebookUid, RPhoneBookSession::EErrorDuringSync);
+
+		iConnectedSession->CompleteRequest(aMessage, phBkInfoRetrievedResult);
+		return;
+		}
+
+	//
+	// Check that the phone store was opened...
+	//
+	RMobilePhoneBookStore  phonebookStore;
+
+	result = iPhonebookManager.GetPhoneBookStore(aPhonebookUid, iPhone,
+												 phonebookStore);
+	if (result != KErrNone)
+		{
+		iPhonebookManager.SetLastSyncError(aPhonebookUid, result);
+   		iPhonebookManager.SetSyncState(aPhonebookUid, RPhoneBookSession::EErrorDuringSync);
+
+		iConnectedSession->CompleteRequest(aMessage, result);
+		return;
+		}
+
+	if (phonebookStore.SubSessionHandle() == 0)
+		{
+		//		
+		// The phonebook is not supported and a sync has been
+		// requested, so remove all entries from the database.
+		//
+		RemoveAllContactsForPhoneBookL(aPhonebookUid);
+
+		iPhonebookManager.SetLastSyncError(aPhonebookUid, KErrNotSupported);
+   		iPhonebookManager.SetSyncState(aPhonebookUid, RPhoneBookSession::EErrorDuringSync);
+
+		iConnectedSession->CompleteRequest(aMessage, KErrNotSupported);
+		return;
+		}
+
+	//
+	// Ensure there is no left sync in progress (there should not be!)...
+	//
+	if (iSyncContactsWithICC != NULL)
+		{
+		iPhonebookManager.SetLastSyncError(aPhonebookUid, KErrServerBusy);
+   		iPhonebookManager.SetSyncState(aPhonebookUid, RPhoneBookSession::EErrorDuringSync);
+
+		iConnectedSession->CompleteRequest(aMessage, KErrServerBusy);
+		return;
+		}
+
+	//
+	// Create the Active Object that will perform the sync...
+	//
+	iSyncContactsWithICC = CSyncContactsWithICC::NewL(*iConnectedSession,
+													  iPhonebookManager,
+													  *iDb,
+													  iPhone,
+													  aPhonebookUid,
+													  aMessage);
+
+	//
+	// Start reading phonebook entries and populating Contacts DB...
+	//	
+	TRAPD(error, iSyncContactsWithICC->SyncContactsWithICCL());
+	if (error != KErrNone)
+		{
+		CompleteDoSync(error);
+		}
+	} // CSyncEngineServer::DoSynchronisationL
+
+
+/**
+ *  Delete an entry specified by aContactId from the ICC phonebook store
+ *  specified. It performs the opperation by creating a CDeleteContactFromICC
+ *  Active Object and starting it.
+ *
+ *  @param aMessage       A reference to the front-end server request.
+ *  @param aPhonebookUid  UID of the phonebook containing the entry.
+ *  @param aContactId     ID of the contact to delete.
+ */
+void CSyncEngineServer::DeleteCntFromICCL(const RMessage2& aMessage,
+										  TUid aPhonebookUid,
+										  TContactItemId  aContactId)
+	{
+	LOGENGINE2(_L8("DeleteCntFromICCL(0%d)"), aContactId);
+
+	//
+	// This request can only proceed if the cache is valid...
+	//
+	RPhoneBookSession::TSyncState  syncState;
+	TInt  result;
+	
+	result = iPhonebookManager.GetSyncState(aPhonebookUid, syncState);
+	if (result != KErrNone)
+		{
+		iConnectedSession->CompleteRequest(aMessage, result);
+		return;
+		}
+	
+	if (syncState != RPhoneBookSession::ECacheValid)
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrNotReady);
+		return;
+		}
+
+	//
+	// Check that ICC is not locked, blocked OR pin protected...
+	//
+	if (iPhonebookManager.IsPin1Valid() == EFalse)
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrAccessDenied);
+		return;
+		}
+
+	//
+	// If this is a USIM App ADN phonebook then check that USIM PIN is valid...
+	//
+	if (aPhonebookUid == KUidUsimAppAdnPhonebook  && 
+		iPhonebookManager.IsUsimAppPinValid() == EFalse)
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrAccessDenied);
+		return;
+		}
+				
+	//
+	// If this is the FDN phonebook then check that PIN2 is valid...
+	//
+	if (aPhonebookUid == KUidIccGlobalFdnPhonebook  && 
+		iPhonebookManager.IsPin2Valid() == EFalse)
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrAccessDenied);
+		return;
+		}
+				
+	//
+	// Now create and run the Active Object which will process this
+	// delete request...
+	//
+	if (iDeleteContactFromICC != NULL)
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrServerBusy);
+		return;
+		}
+
+	iDeleteContactFromICC = CDeleteContactFromICC::NewL(*iConnectedSession,
+														iPhonebookManager,
+														*iDb,
+														iPhone,
+														aPhonebookUid,
+														aMessage); 
+	iDeleteContactFromICC->DoIccDelete(aContactId);
+	} // CSyncEngineServer::DeleteCntFromICCL
+
+
+/**
+ *  Completes an outstanding Delete-from-ICC request. It is called by the
+ *  active object CPhoneBookDoIccDelete class when it has finished its task.
+ *  The functin completes the front-end server's DeleteContact request.
+ * 
+ *  @param aRetVal  Complete-request error code.
+ */
+void CSyncEngineServer::CompleteDoIccDelete(TInt aRetVal)
+	{
+	LOGENGINE2(_L8("CompleteDoIccDelete(%d)"), aRetVal);
+
+	//
+	// Complete the session request and delete the Active Object...
+	//
+	iConnectedSession->CompleteRequest(iDeleteContactFromICC->ClientMessage(),
+									   aRetVal);
+
+	delete iDeleteContactFromICC;
+	iDeleteContactFromICC = NULL;
+	} // CSyncEngineServer::CompleteDoIccDelete
+
+
+/**
+ *  Write an entry to the ICC phonebook store. This is performed by creating an
+ *  Active Object to request the write via ETel. The PIN1 and PIN2 (if FDN)
+ *  must be valid to perform such a request.
+ *
+ *  @param aMessage     A reference to the front-end server request.
+ *  @param aTemplateId  ID of the template to use.
+ *  @param aBufferSize  Size of the streamed ICC buffer.
+ */
+void CSyncEngineServer::WriteCntToICCL(const RMessage2& aMessage,
+									   TContactItemId aTemplateId,
+									   TInt aBufferSize)
+	{
+	LOGENGINE3(_L8("CSyncEngineServer::WriteCntToICCL(): %d 0x%08x %d"),
+			   aTemplateId, aBufferSize);
+
+	//
+	// Is the ICC still in usable state for this phonebook?
+	//
+	TUid  phonebookUid = iPhonebookManager.GetPhonebookUidFromTemplateId(aTemplateId);
+
+	if (iPhonebookManager.IsPin1Valid() == EFalse  ||
+	    (phonebookUid == KUidUsimAppAdnPhonebook  &&
+	     iPhonebookManager.IsUsimAppPinValid() == EFalse)  ||
+	    (phonebookUid == KUidIccGlobalFdnPhonebook  &&
+	     iPhonebookManager.IsPin2Valid() == EFalse))
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrAccessDenied);
+		return;
+		}
+
+	//
+	// Create the Active Object which will process this Write request...
+	//
+	if (iWriteContactToICC != NULL)
+		{
+		iConnectedSession->CompleteRequest(aMessage, KErrServerBusy);
+		return;
+		}
+
+	iWriteContactToICC = CWriteContactToICC::NewL(*iConnectedSession,
+											 	  aBufferSize,
+											 	  iPhonebookManager,
+											 	  iPhone,
+											 	  phonebookUid,
+											 	  aMessage);
+											 	  
+	//
+	// Setup the Active Object and begin...
+	//
+	TRAPD(error, iWriteContactToICC->DoIccWriteL());
+	if (error != KErrNone)
+		{
+		CompleteWriteContactToICC(error);
+		}
+	} // CSyncEngineServer::WriteCntToICCL
+
+
+/**
+ *  Complete an outstanding Write-to-ICC request. It is called by the Active
+ *  Object CWriteContactToICC class when it has finished its task. The ICC
+ *  slot number is written back to the front-end address space and the
+ *  request is completed.
+ *
+ *  @param aRetVal  Complete-request error code.
+ */
+void CSyncEngineServer::CompleteWriteContactToICC(TInt aRetVal)
+	{
+	LOGENGINE2(_L8("CompleteWriteContactToICC(%d)"), aRetVal);
+
+	if (aRetVal == KErrNone)
+		{
+		TPckg<TInt>  indexPckg(iWriteContactToICC->SlotNum());
+		TPckg<TUid>  phonebookUidPckg(iWriteContactToICC->PhonebookUid());
+
+		TRAPD(err1, iWriteContactToICC->ClientMessage().WriteL(1, indexPckg));
+		TRAPD(err2, iWriteContactToICC->ClientMessage().WriteL(2, phonebookUidPckg));
+
+		__ASSERT_ALWAYS(err1 == KErrNone  &&  err2 == KErrNone,
+						PhBkSyncPanic(EPhBkSyncPanicBadDescriptor));
+		}
+
+	//
+	// Complete the session request and delete the Active Object...
+	//
+	iConnectedSession->CompleteRequest(iWriteContactToICC->ClientMessage(),
+									   aRetVal);
+	
+    delete iWriteContactToICC;
+	iWriteContactToICC = NULL;
+	} // CSyncEngineServer::CompleteWriteContactToICC
+
+
+/**
+ *  Cancel a previous synchronisation request.
+ *
+ *  @param aPhonebookUid  UID of the ICC phonebook to cancel the sync request.
+ *
+ *  @return KErrNone if the request was cancelled, otherwise returns an error.
+ */
+TInt CSyncEngineServer::DoSynchronisationCancelL(TUid aPhonebookUid)
+	{
+	LOGENGINE1(_L8("CSyncEngineServer::DoSynchronisationCancelL()"));
+
+	if (iSyncContactsWithICC != NULL  &&
+	    iSyncContactsWithICC->PhonebookUid() == aPhonebookUid)
+		{
+		//
+		// Cancel the Active Object which is used to sync the ICC.
+		// We call DoCancel() rather than Cancel() for two reasons:
+		//
+		//  1) It allows the cancel to be asynchronous, which is important
+		//     for TSY requests that may take a long time.
+		//
+		//  2) It ensures the RunL is called which will call the complete
+		//     function and hence pass the request completion to the client.
+		//
+		iSyncContactsWithICC->DoCancel();
+		}
+
+	return(KErrNone);
+	} // CSyncEngineServer::DoSynchronisationCancelL
+
+
+/**
+ *  Cancel a previous delete request.
+ *
+ *  @param aPhonebookUid  UID of the ICC phonebook to cancel the delete request.
+ *
+ *  @return KErrNone if the request was cancelled, otherwise returns an error.
+ */
+TInt CSyncEngineServer::DeleteCntFromICCCancelL(TUid aPhonebookUid)
+	{
+	LOGENGINE2(_L8("DeleteCntFromICCCancelL(0x%08x)"), aPhonebookUid);
+
+	if (iDeleteContactFromICC != NULL  &&
+		iDeleteContactFromICC->PhonebookUid() == aPhonebookUid)
+		{
+		//
+		// Cancel the Active Object which is used to delete the ICC entry.
+		// We call DoCancel() rather than Cancel() for two reasons:
+		//
+		//  1) It allows the cancel to be asynchronous, which is important
+		//     for TSY requests that may take a long time.
+		//
+		//  2) It ensures the RunL is called which will call the complete
+		//     function and hence pass the request completion to the client.
+		//
+		iDeleteContactFromICC->DoCancel();
+		}
+
+	return(KErrNone);
+	} // CSyncEngineServer::DeleteCntFromICCCancelL
+
+
+/**
+ *  Cancel a previous write request.
+ *
+ *  @param aPhonebookUid  UID of the ICC phonebook to cancel the write request.
+ *
+ *  @return KErrNone if the request was cancelled, otherwise returns an error.
+ */
+TInt CSyncEngineServer::WriteCntToICCCancelL(TUid aPhonebookUid)
+	{
+	LOGENGINE2(_L8("WriteCntToICCCancelL(0x%08x)"), aPhonebookUid);
+
+	if (iWriteContactToICC != NULL  &&
+		iWriteContactToICC->PhonebookUid() == aPhonebookUid)
+		{
+		//
+		// Cancel the Active Object which is used to write the ICC entry.
+		// We call DoCancel() rather than Cancel() for two reasons:
+		//
+		//  1) It allows the cancel to be asynchronous, which is important
+		//     for TSY requests that may take a long time.
+		//
+		//  2) It ensures the RunL is called which will call the complete
+		//     function and hence pass the request completion to the client.
+		//
+		iWriteContactToICC->DoCancel();
+		}
+
+	return(KErrNone);
+	} // CSyncEngineServer::WriteCntToICCCancelL
+
+
+/**
+ *  Connect to the ETel Sever, obtains the name of the currently selected TSY
+ *  and then load the TSY.
+ *
+ *  @return KErrNone if connected successfully, otherwise return error.
+ */
+void CSyncEngineServer::ConnectToEtelL()
+	{
+	LOGENGINE1(_L8("ConnectToEtelL()"));
+	
+	//
+	// Obtain the name of the currently selected TSY...
+	//
+#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY
+	CMDBSession* db = CMDBSession::NewLC(KCDVersion1_2);
+#else
+	CMDBSession* db = CMDBSession::NewLC(KCDVersion1_1);
+#endif
+	
+	CMDBField<TUint32>* globalSettingField = new(ELeave) CMDBField<TUint32>(KCDTIdModemPhoneServicesSMS);
+	CleanupStack::PushL(globalSettingField);
+
+	globalSettingField->SetRecordId(1);
+	globalSettingField->LoadL(*db);
+	TUint32 modemId = *globalSettingField;
+
+	CMDBField<TDesC>* tsyField = new(ELeave) CMDBField<TDesC>(KCDTIdTsyName);
+	CleanupStack::PushL(tsyField);
+
+	tsyField->SetRecordId(modemId);
+	TRAPD(err, tsyField->LoadL(*db));
+	if (err != KErrNone)
+		{
+		LOGENGINE1(_L8("Unable to get default TSY"));
+		}
+	User::LeaveIfError(err);
+
+	TBuf<KCommsDbSvrMaxFieldLength> tsyName;
+	tsyName = *tsyField;
+
+	CleanupStack::PopAndDestroy(3, db); // db, tsyField & globalSettingField
+	
+	TInt ret(iEtelServer.Connect()); // First connect to the ETel server
+	if(ret==KErrNone)
+		{
+		LOGENGINE1(_L8("Loading TSY")); // Now load the TSY 
+		ret=iEtelServer.LoadPhoneModule(tsyName);
+		RTelServer::TPhoneInfo phoneInfo;
+		if(ret==KErrNone)
+			{
+			// Determine if TSY supports V2 functionality
+			// in event of a problem here assume that it does not
+			ret = iEtelServer.IsSupportedByModule(tsyName, KETelExtMultimodeV2, iIsV2Tsy);
+			if (ret != KErrNone)
+				{
+				iIsV2Tsy = EFalse;
+				}
+
+			TInt phoneIndex(0);
+			iEtelServer.EnumeratePhones(phoneIndex); // Get total number of phones supported by all currently loaded TSY modules
+			while(phoneIndex-->0)
+				{
+				TName searchTsyName;
+				// Check whether this phone belongs to loaded TSY
+				if((iEtelServer.GetTsyName(phoneIndex,searchTsyName)==KErrNone) && (searchTsyName.CompareF(tsyName)==KErrNone)) 
+					break;
+				}
+			iEtelServer.GetPhoneInfo(phoneIndex,phoneInfo); // Get information associated with specified phone
+
+			LOGENGINE1(_L8("Open the phone"));
+			ret = iPhone.Open(iEtelServer,phoneInfo.iName); // Open and intialise the phone
+			if (ret!=KErrNone)
+				{
+				LOGENGINE2(_L8("Open phone failed (ret=%d)"), ret);
+				}
+
+			ret = iEtelServer.SetExtendedErrorGranularity(RTelServer::EErrorExtended);
+			if (ret!=KErrNone)
+				{
+				LOGENGINE2(_L8("Cannot request Extended Errors (ret=%d)"), ret);
+				}
+			}
+		else
+			{
+			LOGENGINE2(_L8("Could not load the TSY (ret=%d)"), ret);
+			}
+		}
+	else
+		{
+		LOGENGINE2(_L8("Could not connect to ETel (ret=%d)"), ret);
+		}
+
+	User::LeaveIfError(ret);
+	} // CSyncEngineServer::ConnectToEtelL
+
+
+/**
+ *  Append additional field types according to textual alpha tags.
+ *  The field will contain next types:
+ *  TEL {HOME|WORK} {VOICE|CELL|FAX|....}
+ *
+ *  @param  aTextTag    String that is supposed to contain tags.
+ *  @param  aCurrField  Pointer to the contact item field to which optional
+ *                      types will be added.
+ */
+void CSyncEngineServer::AssignAlphaTagsToFieldTypeL(const TDesC16 &aTextTag,
+												    CContactItemField* aCurrField)
+{
+	//
+	// Exit immediately if there is no field pointer value...
+	//
+	if (aCurrField == NULL) 
+		{
+		return;
+		}
+
+	TFieldType alphaTagUID(KUidContactFieldVCardMapHOME); // Assume HOME by default
+
+	CSyncContactICCEntry::TSyncEntryName textTag(aTextTag);
+	textTag.UpperCase(); // convert text tag(s) to upper case, because later we will use KVersitParam* constants from "cntdef.h"
+
+	//-- try to find out the main type of the phone (WORK/HOME). Assume HOME by default
+	if(textTag.Find(KVersitParamWork)  >= 0) 
+		alphaTagUID = KUidContactFieldVCardMapWORK; //-- found WORK tag, assume the work phone.
+
+	if (!aCurrField->ContentType().ContainsFieldType(alphaTagUID))
+		{
+		aCurrField->AddFieldTypeL(alphaTagUID);        //-- append field type WORK/HOME
+		}
+
+	//-- now try to find more additional tags VOICE/FAX/CELL etc. Assume nothing by default.
+	alphaTagUID = TUid::Null();
+
+	if (textTag.Find(KVersitParamMsg) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapMSG;
+		}
+	else if (textTag.Find(KVersitParamVoice) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapVOICE;
+		}  
+	else if (textTag.Find(KVersitParamFax) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapFAX;
+		}  
+	else if (textTag.Find(KVersitParamPref) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapPREF;
+		}  
+	else if (textTag.Find(KVersitParamCell) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapCELL;
+		}  
+	else if (textTag.Find(KVersitParamPager) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapPAGER;
+		}  
+	else if (textTag.Find(KVersitParamBbs) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapBBS;
+		}  
+	else if (textTag.Find(KVersitParamModem) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapMODEM;
+		}  
+	else if (textTag.Find(KVersitParamCar) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapCAR;
+		}  
+	else if (textTag.Find(KVersitParamIsdn)  >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapISDN;
+		}  
+	else if (textTag.Find(KVersitParamVideo) >= 0)
+		{
+		alphaTagUID = KUidContactFieldVCardMapVIDEO;
+		}  
+
+	if (alphaTagUID != TUid::Null())
+		{
+		//
+		// Append additional field type...
+		//
+		if (!aCurrField->ContentType().ContainsFieldType(alphaTagUID))
+			{
+			aCurrField->AddFieldTypeL(alphaTagUID);
+			}
+		}
+	} // CSyncEngineServer::AssignAlphaTagsToFieldTypeL
+
+
+/**
+ *  Decide whether the entry needs to be added to the contacts database or
+ *  the existing one needs to be updated by comparing it to the one already
+ *  stored in the database. 
+ *
+ *  Work out the write action required for this entry.
+ *
+ *  There are 3 possibilities when searching for this item in the lookup table:
+ *    1 - item is not found - needs to be added
+ *    2 - item is found and identical - no action required
+ *    3 - item is found and different - needs to be updated
+ *
+ *
+ *  Create an CContactICCEntry using the data provided by aContactItem and then
+ *  write the entry to the Contacts Database.
+ *
+ *  If it is a new entry then create an CContactICCEntry using the data provided
+ *  by aContactItem and then write to the Contacts Database. Otherwise edit the
+ *  existing ICC contact item and commit the changes to the database. 
+ * 
+ *  If the entry is hidden it will be stored in the contacts database, but the
+ *  server will also set the contact item's hidden attribute.
+ *
+ *  This means that the item will not be displayed if the view definition
+ *  excludes hidden fields. 
+ *
+ * @param aICCEntry  Phonebook Server internal format for ICC Entry 
+ *
+ * @return Contacts ID if operation was successful, otherwise a NULL ID.
+ */
+TContactItemId CSyncEngineServer::WriteICCContactToDBL(CSyncContactICCEntry& aICCEntry)
+	{
+	LOGENGINE2(_L8("WriteICCContactToDBL(): 0x%08x"), aICCEntry.iPhonebookUid);
+	
+	//
+	// Check if the ID does not yet exist, or is identical, to see what needs
+	// to be done.
+	//
+	TInt  result;
+	
+	result = iPhonebookManager.GetContactIdFromSlotNum(aICCEntry.iPhonebookUid,
+													   aICCEntry.iSlotNum,
+													   aICCEntry.iContactId);
+	if (result != KErrNone  &&  result != KErrNotFound)
+		{
+		User::Leave(result);
+		}
+
+	if (result == KErrNotFound)
+		{
+		//
+		// The contact doesn't exist, we will continue and add it...
+		//
+		LOGENGINE2(_L8("WriteICCContactToDBL(): Slot %d add"), aICCEntry.iSlotNum);
+		aICCEntry.iContactId = KNullContactId;
+		}
+	else if (EntriesIdenticalL(aICCEntry, aICCEntry.iContactId))
+		{
+		//
+		// The contact exists, and is identical so there is nothing to do. We
+		// can return now...
+		//
+		LOGENGINE2(_L8("WriteICCContactToDBL(): Slot %d nothing to do"), aICCEntry.iSlotNum);
+
+		return aICCEntry.iContactId;
+		}
+	else
+		{
+		//
+		// The entry needs to be updated. In this case, we will delete it now and then
+		// continue and add it again (which is easier than clearing out all the fields
+		// and lists etc.)...
+		//
+		LOGENGINE2(_L8("WriteICCContactToDBL(): Slot %d delete and add"), aICCEntry.iSlotNum);
+
+		iDb->DeleteContactL(aICCEntry.iContactId);
+		aICCEntry.iContactId = KNullContactId;
+		}
+
+	//
+	// Get the template and group IDs...
+	//
+	TContactItemId  templateId;
+	TContactItemId  groupId;
+			
+	User::LeaveIfError(iPhonebookManager.GetTemplateId(aICCEntry.iPhonebookUid, templateId));
+	User::LeaveIfError(iPhonebookManager.GetGroupId(aICCEntry.iPhonebookUid, groupId));
+
+	//
+	// Create template for ICC contacts items...
+	//
+	CContactItem* iccTemplate = iDb->ReadContactL(templateId);
+	CleanupStack::PushL(iccTemplate); 
+
+	CContactICCEntry*  item = CContactICCEntry::NewL(*iccTemplate);
+	CleanupStack::PopAndDestroy(iccTemplate); 
+	CleanupStack::PushL(item); 
+
+	//
+	// Create phone number field(s)
+	//
+
+	// Default number 
+	if (aICCEntry.iNumber.Length() != 0)
+		{
+		TBuf<RMobilePhone::KMaxMobileTelNumberSize>  number;
+		if ((aICCEntry.iTON==RMobilePhone::EInternationalNumber) && (aICCEntry.iNumber[0] != KEngineInternationalPrefix()[0]))
+			{
+			number.Append(KEngineInternationalPrefix); // Append '+' prefix if International Number
+			}
+		number.Append(aICCEntry.iNumber);
+		AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldPhoneNumber, KUidContactFieldVCardMapTEL, number, item, 0);
+		}
+
+	// Check whether this is hidden entry and set its hidden attributes 
+	if (aICCEntry.iIsHidden)
+		{
+		item->SetHidden(ETrue);
+		}
+
+	// Additional numbers 
+	TInt count(aICCEntry.iNumberList->Count());
+	for (TInt i=0; i<count; ++i)
+		{
+		CSyncContactICCEntry::TSyncAdditionalNumber  additionalNum(aICCEntry.iNumberList->At(i));
+
+		// Actual number
+		TBuf<RMobilePhone::KMaxMobileTelNumberSize> number;
+		if((additionalNum.iTON==RMobilePhone::EInternationalNumber) && (additionalNum.iNumber[0] != KEngineInternationalPrefix()[0])) 
+			{
+			number.Append(KEngineInternationalPrefix); // Append '+' prefix if International Number
+			}	
+		number.Append(additionalNum.iNumber);
+
+		//-- add or modify existing text field in CContactICCEntry* item fieldset
+		CContactItemField *pCurrField = 
+		  AddTextFieldToIccContactL(KStorageTypeText, 
+					    KUidContactFieldPhoneNumber, 
+					    KUidContactFieldVCardMapTEL, 
+					    number, 
+					    item, 
+					    i + 1);   // Here, 1 is used to distinguish the 
+		                                      // default number and additional number. 
+		                                      // The reason is that they share the
+		                                      // same field type (see comments in 
+		                                      // method AddTextFieldToIccContactL).
+
+		
+		//-- append additional field type to the current field if any.
+		//-- this field type is obtained from additional number alpha tag 
+		if(pCurrField)
+			{
+		 	AssignAlphaTagsToFieldTypeL(additionalNum.iNumberString, pCurrField);
+			}
+		}
+
+	// Create name field
+	if (aICCEntry.iName.Length() > 0)
+		{
+		AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldFamilyName, KUidContactFieldVCardMapUnusedN, aICCEntry.iName, item, 0);
+		}
+
+	// Create second name field
+	if (aICCEntry.iSecondName.Length() > 0)
+		{
+		AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldSecondName, KUidContactFieldVCardMapSECONDNAME, aICCEntry.iSecondName, item, 0);
+		}
+
+	// Create group field(s)
+	count = aICCEntry.iGroupList->Count();
+	for(TInt i=0; i<count; ++i)
+		{
+		CSyncContactICCEntry::TSyncEntryName groupField;
+		groupField.Copy(aICCEntry.iGroupList->At(i));
+		AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldICCGroup, KUidContactFieldVCardMapUnusedN, groupField, item, i);
+		}
+
+	// Create e-mail field(s)
+	count = aICCEntry.iEmailList->Count();
+	for(TInt i=0; i<count; ++i)
+		{
+		CSyncContactICCEntry::TSyncEntryName emailField;
+		emailField.Copy(aICCEntry.iEmailList->At(i));
+		AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldEMail, KUidContactFieldVCardMapEMAILINTERNET, emailField, item, i);
+		}
+
+	// Create slot number field
+	TBuf<KTemplateFieldLength> buf;
+	buf.AppendNum(aICCEntry.iSlotNum);
+	AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldICCSlot, KUidContactFieldVCardMapNotRequired, buf, item, 0);
+
+	// Create phonebook type field
+	buf.FillZ();
+	buf.Zero();
+
+	buf.AppendNum(aICCEntry.iPhonebookUid.iUid);
+	AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldICCPhonebook, KUidContactFieldVCardMapNotRequired, buf, item, 0);
+
+	//
+	// Write the contact to the database...
+	//
+	LOGENGINE1(_L8("WriteICCContactToDBL(): Adding contact to database"));
+
+	iDb->DatabaseBeginL(EFalse); // Start the transaction
+	iIsInTransaction = ETrue; 
+
+	aICCEntry.iContactId = iDb->doAddNewContactL(*item, EFalse, ETrue); // Add that new contact to Contact DB 
+	CleanupStack::PopAndDestroy(item);
+
+	// Add the contact to the correct group now because during
+	// synchronisation the contacts model can't do it since the 
+	// plug-in won't have a connection to the server (and therefore know 
+	// which group to add it to).
+	iDb->AddContactToGroupL(aICCEntry.iContactId, groupId, ETrue);
+	CommitIccTransactionL();			
+
+	return aICCEntry.iContactId;
+	} // CSyncEngineServer::WriteICCContactToDBL
+
+
+/**
+ *  Checks if an ICC entry is the same as one in the database.
+ *
+ *  @param aICCEntry  CSyncContactICCEntry reference.
+ *  @param aId        TContactItemId reference.
+ *
+ *  @return Boolean stating if entries match.
+ */
+TBool CSyncEngineServer::EntriesIdenticalL(CSyncContactICCEntry& aICCEntry,
+										   TContactItemId& aId)
+	{
+	LOGENGINE3(_L8("EntriesIdenticalL(): 0x%08x %d"), aICCEntry.iPhonebookUid,
+			   aId);
+	
+	//
+	// Get the entry from the contacts database...
+	//
+	CContactItem* contactEntry=iDb->ReadContactLC(aId);
+	CContactItemFieldSet& fieldSet = contactEntry->CardFields();
+
+	// Note: We have already matched the slot number field.
+	
+	// Check if the same amount of information is provided.
+	// This will reduce time for comparison when there is a lot of additional
+	// information but a small mismatch in the number of additional fields.
+	TInt index;
+	
+	// Obtain number count
+	TInt iccNumCount(aICCEntry.iNumberList->Count() + 1); // first phone number is not in additional number list
+	TInt contactNumCount(0);
+	index = -1;
+	while((index = fieldSet.FindNext(KUidContactFieldPhoneNumber, index+1)) != KErrNotFound)
+		{
+		contactNumCount++;
+		}
+	
+	// Obtain group count
+	TInt iccGrpCount(aICCEntry.iGroupList->Count());
+	TInt contactGrpCount(0);
+	index = -1;
+	while((index = fieldSet.FindNext(KUidContactFieldICCGroup, index+1)) != KErrNotFound)
+		{
+		const CContactItemField& testField = fieldSet[index];
+		TPtrC testFieldPtr(testField.TextStorage()->Text());
+		if (testFieldPtr.Length() > 0) // entry for SDN & LND will have group field but it will be empty
+			{
+			contactGrpCount++;
+			}
+		}
+		
+	// Obtain email count
+	TInt iccEmailCount(aICCEntry.iEmailList->Count());
+	TInt contactEmailCount(0);
+	index = -1;
+	while((index = fieldSet.FindNext(KUidContactFieldEMail, index+1)) != KErrNotFound)
+		{
+		const CContactItemField& testField = fieldSet[index];
+		TPtrC testFieldPtr(testField.TextStorage()->Text());
+		if (testFieldPtr.Length() > 0)  // entry for SDN & LND will have email field but it will be empty
+			{
+			contactEmailCount++;
+			}
+		}
+		
+	// Test field counts and exit if these don't match
+	if(	(contactNumCount != iccNumCount) ||
+		(contactGrpCount != iccGrpCount) ||
+		(contactEmailCount != iccEmailCount) ||
+		(contactNumCount < 1))
+		{
+		CleanupStack::PopAndDestroy(contactEntry);
+		return EFalse;
+		}
+		
+	// Detailed test of first representation of name
+	TInt loop(0);
+	TBool contentMatch(ETrue);
+
+	index = fieldSet.Find(KUidContactFieldFamilyName); // start with family name
+	if (index != KErrNotFound) 
+		{
+		const CContactItemField& testField1 = fieldSet[index];
+		TPtrC testFieldPtr1(testField1.TextStorage()->Text());
+		if (testFieldPtr1.Compare(aICCEntry.iName) != 0)
+			{
+			contentMatch = EFalse;
+			}
+		}
+	else if (aICCEntry.iName.Length() > 0)
+		{
+		contentMatch = EFalse;
+		}
+
+	// Detailed test of second representation of name
+	if (contentMatch)
+		{
+		index = fieldSet.Find(KUidContactFieldSecondName); // get second name
+		if (index != KErrNotFound)
+			{
+			const CContactItemField& testField1 = fieldSet[index];
+			TPtrC testFieldPtr1(testField1.TextStorage()->Text());
+			if (testFieldPtr1.Compare(aICCEntry.iSecondName) != 0)
+				{
+				contentMatch = EFalse;
+				}
+			}
+		else if (aICCEntry.iSecondName.Length() > 0)
+			{
+			contentMatch = EFalse;
+			}
+		}
+
+	if (contentMatch)
+		{
+		// Detailed test of phone number information
+		// Note: the first phone number is stored separately from additional numbers
+		//       and as a result this loop treats it differently.  The loop counter
+		//       is adjusted to allow for this number not being in the additional number list
+		index = fieldSet.Find(KUidContactFieldPhoneNumber);
+		for (loop = 0;
+			 (loop < iccNumCount) && (index != KErrNotFound) && contentMatch; 
+			 loop++)
+			{
+			const CContactItemField& testField2 = fieldSet[index];
+			TBuf<RMobilePhone::KMaxMobileTelNumberSize> testFieldPtr2 = testField2.TextStorage()->Text();
+			TBool isInternationalCntNumber(EFalse);
+
+			if (testFieldPtr2.Find(KEngineInternationalPrefix)!=KErrNotFound)
+				{
+				testFieldPtr2 = testFieldPtr2.Mid(1);
+				isInternationalCntNumber = ETrue;
+				}
+
+			// retrieving the number from the ICC entry
+			TBool isInternationalICCNumber;
+			TBuf<RMobilePhone::KMaxMobileTelNumberSize> testFieldPtr5; 
+			if (loop == 0) // first phone number is stored separately
+				{
+				testFieldPtr5 = aICCEntry.iNumber;
+				isInternationalICCNumber = (aICCEntry.iTON==RMobilePhone::EInternationalNumber);
+				}
+			else 
+				{
+				testFieldPtr5 = aICCEntry.iNumberList->At(loop - 1).iNumber;
+				isInternationalICCNumber = (aICCEntry.iNumberList->At(loop - 1).iTON==RMobilePhone::EInternationalNumber);
+				}
+
+			// Does the ICC number contain a prefix? (apparently, some TSYs are designed to keep it)
+			if (testFieldPtr5.Find(KEngineInternationalPrefix)!=KErrNotFound)
+				{
+				// If it does, strip it
+				testFieldPtr5 = testFieldPtr5.Mid(1);
+				// Also implies that it is international too, regardless of the aICCEntry.iTON flag value
+				isInternationalICCNumber = ETrue;
+				}
+			
+			// comparing the numbers
+			if (  	(isInternationalCntNumber!=isInternationalICCNumber) ||
+					(testFieldPtr2.Compare(testFieldPtr5) != 0) ) 
+				{
+				contentMatch = EFalse;
+				}
+			index = fieldSet.FindNext(KUidContactFieldPhoneNumber, index+1);  // get next additional number
+			}
+
+		if (contentMatch)
+			{
+			// Detailed test of group information
+			index = fieldSet.Find(KUidContactFieldICCGroup);
+			for (	loop = 0;
+					(loop < iccGrpCount) && (index != KErrNotFound) && contentMatch; 
+					loop++)
+				{
+				const CContactItemField& testField3 = fieldSet[index];
+				TPtrC testFieldPtr3(testField3.TextStorage()->Text());
+
+				// Ignore blank group entries...
+				if (testFieldPtr3.Length() == 0)
+					{
+					index = fieldSet.FindNext(KUidContactFieldICCGroup, index+1);
+					loop--;
+					continue;
+					}
+
+				if (testFieldPtr3.Compare(aICCEntry.iGroupList->At(loop)) != 0)
+					{
+					contentMatch = EFalse;
+					}
+				index = fieldSet.FindNext(KUidContactFieldICCGroup, index+1);
+				}
+			
+			if (contentMatch)
+				{
+				// Detailed test of Email information
+				index = fieldSet.Find(KUidContactFieldEMail);
+				for (loop = 0;
+					 (loop < iccEmailCount) && (index != KErrNotFound) && contentMatch; 
+					 loop++)
+					{
+					const CContactItemField& testField4 = fieldSet[index];
+					TPtrC testFieldPtr4(testField4.TextStorage()->Text());
+
+					// Ignore blank email entries...
+					if (testFieldPtr4.Length() == 0)
+						{
+						index = fieldSet.FindNext(KUidContactFieldEMail, index+1);
+						loop--;
+						continue;
+						}
+
+					if (testFieldPtr4.Compare(aICCEntry.iEmailList->At(loop)) != 0)
+						{
+						contentMatch = EFalse;
+						}
+					index = fieldSet.FindNext(KUidContactFieldEMail, index+1);
+					}
+				}
+			}
+		}
+
+	// Cleanup and return result		
+	CleanupStack::PopAndDestroy(contactEntry);
+
+	return contentMatch;
+	} // CSyncEngineServer::EntriesIdenticalL
+
+
+/**
+ *  Commit the current transaction. 
+ */
+void CSyncEngineServer::CommitIccTransactionL()
+	{
+	if (iIsInTransaction)
+		{
+		LOGENGINE1(_L8("Commit Icc Transaction"));
+		iDb->DatabaseCommitL(EFalse);
+		iIsInTransaction =  EFalse;
+		}
+	} // CSyncEngineServer::CommitIccTransactionL
+
+
+/**
+ *  Rollback the current transaction. 
+ */
+void CSyncEngineServer::RollbackIccTransaction()
+	{
+	if (iIsInTransaction)
+		{
+		LOGENGINE1(_L8("Rollback Icc Transaction"));
+
+		//
+		// Transaction must be rolled back if a failure occurs otherwise
+		// Contacts Database will become corrupt...
+		//
+		iDb->DatabaseRollback();
+
+		//
+		// Check whether database is damaged and needs to be recovered. This
+		// should never happen...
+		//
+		if (iDb->IsDamaged())
+			{
+			TRAPD(recoverErr, iDb->RecoverL());
+			if (recoverErr == KErrDiskFull)
+				{
+				//
+				// The disk is full, compact the database and check for
+				// damage again. Not much else we can do!
+				//
+				TRAPD(compactErr, iDb->CompactL());
+
+				if (compactErr != KErrNone  &&  iDb->IsDamaged())
+					{
+					TRAPD(recoverErr2, iDb->RecoverL());
+					if (recoverErr2 == KErrDiskFull)
+						{
+						TRAP_IGNORE(iDb->CompactL());
+						}
+					}
+				}
+			}
+		
+		//
+		// We're no longer in a transaction...
+		//
+		iIsInTransaction = EFalse;
+		}
+	} // CSyncEngineServer::RollbackIccTransaction
+
+
+/**
+ *  Add a new text field (aField) to the CContactICCEntry supplied by aIccEntry. 
+ *
+ * @param aType       Field Storage type 
+ * @param aFieldType  Field type
+ * @param aMapping    Mapping for the field's content type
+ * @param aField      Field data
+ * @param aIccEntry   CContactICCEntry item
+ * @param aCount      Identifies which instance of field is to be used. Actually
+ *					  it is an index of an object in one of vectors from
+ *					  CSyncContactICCEntry class.
+ *
+ * @return            Pointer to the current (modified or added) field in
+ *                    aIccEntry fieldset so that it is possible to get access
+ *                    to this field later. Can be NULL.
+ */ 
+CContactItemField* CSyncEngineServer::AddTextFieldToIccContactL(TStorageType aType,
+																TFieldType aFieldType,
+																TUid aMapping,
+																TDesC& aField,
+																CContactICCEntry* aIccEntry,
+																TInt aCount)
+	{
+	CContactItemFieldSet& fieldSet = aIccEntry->CardFields();
+	TInt pos(KErrNotFound);
+	for (TInt i = 0; i <= aCount; i++)
+		{
+		// "pos+1" below provides a new start position for the FindNext.
+		pos = fieldSet.FindNext(aFieldType, pos + 1); 
+		if (pos == KErrNotFound)
+			break;
+		}
+
+    CContactItemField   *pCurrField = NULL;  // pointer to the current field in fieldset
+
+    if (pos!=KErrNotFound) // Field already present. Note, Contacts model reads all fields
+			   // in template and adds them as empty fields in ICC item
+		{
+		// If the "aCount" identified instance field already exists in the "fieldSet",
+		// then update it.
+
+		CContactItemField& field=fieldSet[pos];
+		field.TextStorage()->SetTextL(aField);
+		
+        pCurrField = &field;
+        }
+	else
+		{
+		// If there is no field in the "fieldSet" whose type is the given "aFieldType",
+		// then create a new field and add it into the "fieldSet". 
+		// Or
+		// If the "aCount" identified instance field does not exist in the "fieldSet",
+		// then create a new field and add it into the "fieldSet".
+
+		CContactItemField* field=CContactItemField::NewLC(aType, aFieldType);
+		field->SetMapping(aMapping);
+		field->AddFieldTypeL(aFieldType); // Appends a field type to the field's content type
+		field->TextStorage()->SetTextL(aField);
+		aIccEntry->AddFieldL(*field);
+		CleanupStack::Pop(field); // No need to destroy it since contact item takes ownership of field
+		
+        pCurrField = field;
+        }
+
+	return pCurrField;
+	} // CSyncEngineServer::AddTextFieldToIccContactL
+
+
+/**
+ *  Create a new client session.
+ */
+CSession2* CSyncEngineServer::NewSessionL(const TVersion& /*aVersion*/,
+										  const RMessage2& /*aMessage*/) const
+	{
+	LOGENGINE1(_L8("CSyncEngineServer::NewSessionL"));
+
+	//
+	// Only one session connection is allowed!!!
+	//
+	if (iConnectedSession != NULL)
+		{
+		User::Leave(KErrPermissionDenied);
+		}
+		
+	return new(ELeave) CSyncEngineSession();
+	} // CSyncEngineServer::NewSessionL
+
+
+/**
+ *  Called when a new session is being created.
+ *
+ *  @param aSession  Server side session.
+ */
+void CSyncEngineServer::AddSessionL(CSyncEngineSession* aSession)
+	{
+	LOGENGINE1(_L8("CSyncEngineServer::AddSession"));
+	
+	//
+	// Store the session pointer...
+	//
+	iConnectedSession = aSession;
+
+	//
+	// Queue an Active Object to configure the engine straight after this
+	// session is created.
+	//
+	if (iPhoneBookSyncEngineStarter == NULL)
+		{
+		iPhoneBookSyncEngineStarter = new (ELeave) CPhoneBookSyncEngineStarter(*this);
+		iPhoneBookSyncEngineStarter->Call();
+		}
+	} // CSyncEngineServer::AddSessionL
+
+
+/**
+ *  Called when a session is being destroyed.
+ */
+void CSyncEngineServer::DropSession(CSyncEngineSession* /*aSession*/)
+	{
+	LOGENGINE1(_L8("CSyncEngineServer::DropSession"));
+
+	iConnectedSession = NULL;
+	TRAP_IGNORE(UnconfigureEngineL());
+
+	CActiveScheduler::Stop();
+	} // CSyncEngineServer::DropSession
+
+
+/**
+ *  Standard Active Object RunError() method, called when the RunL() method
+ *  leaves, which will be when the CPhoneBookSession::ServiceL() leaves.
+ *
+ *  Find the current message and complete it before restarting the server.
+ *
+ *  @param aError  Leave code from CPhoneBookSession::ServiceL().
+ *
+ *  @return KErrNone
+ */
+TInt CSyncEngineServer::RunError(TInt aError)
+	{
+	LOGENGINE2(_L8("CSyncEngineServer::RunError %d"),aError);
+
+	//
+	// Complete the request with the available error code.
+	//
+	if (Message().IsNull() == EFalse)
+		{
+		Message().Complete(aError);
+		}
+
+	//
+	// The leave will result in an early return from CServer::RunL(), skipping
+	// the call to request another message. So do that now in order to keep the
+	// server running.
+	//
+	ReStart();
+	
+	return KErrNone;
+	} // CSyncEngineServer::RunError
+
+
+/**
+ *  This method begins the completion of a synchronisation operation. It is
+ *  called when the CSyncContactsWithICC Active Object has finished its task.
+ *
+ *  @param aRetVal  Complete-request error code.
+ */
+void CSyncEngineServer::CompleteDoSync(TInt aRetVal)
+	{
+	TUid  phonebookUid = iSyncContactsWithICC->PhonebookUid();
+	
+	LOGENGINE3(_L8("CompleteDoSync(%d): 0x%08x"), aRetVal, phonebookUid);
+
+	//
+	// Store the result of the synchronisation operation...
+	//
+	if (aRetVal == KErrNone)
+		{
+		iPhonebookManager.SetLastSyncError(phonebookUid, KErrNone);
+   		iPhonebookManager.SetSyncState(phonebookUid,
+   									   RPhoneBookSession::ECacheValid);
+		}
+	else if (aRetVal == KErrCancel)
+		{
+		iPhonebookManager.SetLastSyncError(phonebookUid, KErrCancel);
+   		iPhonebookManager.SetSyncState(iSyncContactsWithICC->PhonebookUid(),
+   									   RPhoneBookSession::EUnsynchronised);
+		}
+   	else
+		{
+		iPhonebookManager.SetLastSyncError(phonebookUid, aRetVal);
+   		iPhonebookManager.SetSyncState(phonebookUid,
+   									   RPhoneBookSession::EErrorDuringSync);
+		}
+
+	//
+	// For debug purposes print out the new Look Up Table for this phonebook...
+	//
+#ifdef _DEBUG
+	iPhonebookManager.LogLookUpTable(iSyncContactsWithICC->PhonebookUid());
+#endif	
+
+	//
+	// Complete this request and release the Active Object's memory...
+	//
+	iConnectedSession->CompleteRequest(iSyncContactsWithICC->ClientMessage(),
+									   aRetVal);
+
+	delete iSyncContactsWithICC;
+	iSyncContactsWithICC = NULL;
+	} // CSyncEngineServer::CompleteDoSync
+
+
+/**
+ *  Create and install the active scheduler.
+ */
+CSyncEngineScheduler* CSyncEngineScheduler::New()
+	{
+	LOGENGINE1(_L8("CSyncEngineScheduler::New()"));
+
+	CSyncEngineScheduler*  scheduler = new CSyncEngineScheduler;
+
+	if (scheduler != NULL)
+		{
+		CSyncEngineScheduler::Install(scheduler);
+		}
+
+	return scheduler;
+	} // CSyncEngineScheduler::New
+
+
+/**
+ *  Called if any RunL() method leaves.
+ */
+void CSyncEngineScheduler::Error(TInt aError) const
+	{
+#ifdef _DEBUG
+	LOGENGINE2(_L8("CSyncEngineScheduler::Error(%d)"), aError);
+#else
+	(void) aError;
+#endif
+
+	PhBkSyncPanic(EPhBkSyncPanicUnexpectedLeave);
+	} // CSyncEngineScheduler::Error
+
+
+/**
+ *  Standard constructor.
+ *
+ *  @param aEngine  Reference to the engine.
+ */
+CPhoneBookSyncEngineStarter::CPhoneBookSyncEngineStarter(CSyncEngineServer& aEngine)
+  : CAsyncOneShot(EPriorityHigh),
+    iEngine(aEngine)
+	{
+	// NOP
+	} // CPhoneBookSyncEngineStarter::CPhoneBookSyncEngineStarter
+
+
+/**
+ *  RunL() for the starter object. This configures the engine fully or
+ *  shuts it down.
+ */
+void CPhoneBookSyncEngineStarter::RunL()
+	{
+	LOGENGINE2(_L8("CPhoneBookSyncEngineStarter::RunL(): iStatus=%d."), iStatus.Int());
+
+	//
+	// Configure the engine...
+	//
+	TRAPD(configErr, iEngine.ConfigureEngineL());
+	if (configErr != KErrNone)
+		{
+		LOGENGINE2(_L8("ConfigureEngineL() failed with error %d."), configErr);
+
+		//
+		// Shutdown the engine in this case, so it can be restarted later.
+		//
+		TRAP_IGNORE(iEngine.UnconfigureEngineL());
+		CActiveScheduler::Stop();
+		}
+	} // CPhoneBookSyncEngineStarter::RunL
+