diff -r 000000000000 -r e686773b3f54 pimprotocols/phonebooksync/Server/SyncEngineServer.cpp --- /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 +#include +#include + +#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 (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 (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 (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(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(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(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 indexPckg(iWriteContactToICC->SlotNum()); + TPckg 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* globalSettingField = new(ELeave) CMDBField(KCDTIdModemPhoneServicesSMS); + CleanupStack::PushL(globalSettingField); + + globalSettingField->SetRecordId(1); + globalSettingField->LoadL(*db); + TUint32 modemId = *globalSettingField; + + CMDBField* tsyField = new(ELeave) CMDBField(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 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 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; iAt(i)); + + // Actual number + TBuf 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; iAt(i)); + AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldICCGroup, KUidContactFieldVCardMapUnusedN, groupField, item, i); + } + + // Create e-mail field(s) + count = aICCEntry.iEmailList->Count(); + for(TInt i=0; iAt(i)); + AddTextFieldToIccContactL(KStorageTypeText, KUidContactFieldEMail, KUidContactFieldVCardMapEMAILINTERNET, emailField, item, i); + } + + // Create slot number field + TBuf 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 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 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 +