diff -r 2bb96f4ecad8 -r 8e7494275d3a omads/omadsextensions/adapters/contactsgroup/src/contactsgrpdatastore.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omads/omadsextensions/adapters/contactsgroup/src/contactsgrpdatastore.cpp Tue Aug 31 15:05:37 2010 +0300 @@ -0,0 +1,1268 @@ +/* +* Copyright (c) 2009-2010 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: Part of SyncML Data Synchronization Plug In Adapter +* +*/ + + +#include +#include + +#include "contactsgrpdatastore.h" +#include "contactsgrpconverter.h" +#include "changefinder.h" +#include "contactsgrpdataproviderdefs.h" +#include "logger.h" + +const TInt KDefaultBufferSize = 1024; +const TInt KDataBufferNotReady = -1; +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::CContactsGrpDataStore +// C++ default constructor can NOT contain any code, that might leave. +// ----------------------------------------------------------------------------- +CContactsGrpDataStore::CContactsGrpDataStore() : + iKey( TKeyArrayFix( _FOFF( TNSmlSnapshotItem, ItemId() ), ECmpTInt ) ) + { + TRACE_FUNC; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::ConstructL +// Symbian 2nd phase constructor, can leave. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::ConstructL() + { + TRACE_FUNC_ENTRY; + + User::LeaveIfError( iFs.Connect() ); + + iNewItems = new ( ELeave ) CNSmlDataItemUidSet; + iDeletedItems = new ( ELeave ) CNSmlDataItemUidSet; + iUpdatedItems = new ( ELeave ) CNSmlDataItemUidSet; + iEmptyList = new ( ELeave ) CNSmlDataItemUidSet; + + iConverter = CContactsGrpConverter::NewL(); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::~CContactsGrpDataStore +// Destructor +// ----------------------------------------------------------------------------- +CContactsGrpDataStore::~CContactsGrpDataStore() + { + TRACE_FUNC_ENTRY; + + delete iDataBuffer; + delete iNewItems; + delete iDeletedItems; + delete iUpdatedItems; + delete iEmptyList; + + if ( iChangeFinder ) + { + TRAPD( error, iChangeFinder->CloseL() ); + if ( error != KErrNone ) + { + LOGGER_WRITE_1( "iChangeFinder->CloseL() leaved with %d", error ); + } + } + delete iChangeFinder; + + delete iConverter; + + delete iContactsDb; + + iFs.Close(); + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +CContactsGrpDataStore* CContactsGrpDataStore::NewL() + { + TRACE_FUNC_ENTRY; + CContactsGrpDataStore* self = CContactsGrpDataStore::NewLC(); + CleanupStack::Pop( self ); + TRACE_FUNC_EXIT; + return self; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::NewLC +// Two-phased constructor. +// ----------------------------------------------------------------------------- +CContactsGrpDataStore* CContactsGrpDataStore::NewLC() + { + TRACE_FUNC_ENTRY; + CContactsGrpDataStore* self = new ( ELeave ) CContactsGrpDataStore(); + CleanupStack::PushL( self ); + self->ConstructL(); + TRACE_FUNC_EXIT; + return self; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoOpenL +// Opens database. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoOpenL( const TDesC& /*aStoreName*/, + MSmlSyncRelationship& aContext, TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + if ( iContactsDb ) + { + // already opened + User::RequestComplete( iCallerStatus, KErrInUse ); + LOGGER_WRITE( "CContactsGrpDataStore::DoOpenL failed with KErrInUse." ); + return; + } + + // Create ChangeFinder + if ( iChangeFinder ) + { + delete iChangeFinder; + iChangeFinder = NULL; + } + iChangeFinder = CChangeFinder::NewL( aContext, iKey, iHasHistory ); + + iContactsDb = CContactDatabase::OpenL(); + + RegisterSnapshotL(); + + User::RequestComplete( iCallerStatus, KErrNone ); + TRACE_FUNC_EXIT; + } + + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCancelRequest +// Not supported, does nothing. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCancelRequest() + { + TRACE_FUNC; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoStoreName +// Returns the name of the DataStore +// ----------------------------------------------------------------------------- +const TDesC& CContactsGrpDataStore::DoStoreName() const + { + TRACE_FUNC; + + if ( iContactsDb ) + { + return KContactsGrpStoreName; + } + else + { + return KNullDesC; + } + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoBeginTransactionL +// Transactions are not supported. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoBeginTransactionL() + { + TRACE_FUNC; + User::Leave( KErrNotSupported ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCommitTransactionL +// Transactions are not supported. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCommitTransactionL( TRequestStatus& aStatus ) + { + TRACE_FUNC; + iCallerStatus = &aStatus; + User::RequestComplete( iCallerStatus, KErrNotSupported ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoRevertTransaction +// Transactions are not supported. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoRevertTransaction( TRequestStatus& aStatus ) + { + TRACE_FUNC; + iCallerStatus = &aStatus; + User::RequestComplete( iCallerStatus, KErrNotSupported ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoBeginBatchL +// Batching is not supported. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoBeginBatchL() + { + TRACE_FUNC; + User::Leave( KErrNotSupported ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCommitBatchL +// Batching is not supported +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCommitBatchL( RArray& /*aResultArray*/, TRequestStatus& aStatus ) + { + TRACE_FUNC; + iCallerStatus = &aStatus; + User::RequestComplete( iCallerStatus, KErrNotSupported ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCancelBatch +// Batching is not supported +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCancelBatch() + { + TRACE_FUNC; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoSetRemoteStoreFormatL +// Not supported +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoSetRemoteStoreFormatL( const CSmlDataStoreFormat& /*aServerDataStoreFormat*/ ) + { + TRACE_FUNC; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoSetRemoteMaxObjectSize +// Not supported +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoSetRemoteMaxObjectSize( TInt /*aServerMaxObjectSize*/ ) + { + TRACE_FUNC; + } + + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoMaxObjectSize +// Reads the maximum object size from the central repository +// ----------------------------------------------------------------------------- +TInt CContactsGrpDataStore::DoMaxObjectSize() const + { + TRACE_FUNC; + return 0; // no limit + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoOpenItemL +// Opens item in the DataStore, reads it (either completely or partially) +// to the temporary buffer where it can be later read to the remote database. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoOpenItemL( + TSmlDbItemUid aUid, + TBool& aFieldChange, + TInt& aSize, + TSmlDbItemUid& aParent, + TDes8& aMimeType, + TDes8& aMimeVer, + TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "aUid: %d", aUid ); + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + aFieldChange = EFalse; + aParent = KErrNotFound; + + delete iDataBuffer; + iDataBuffer = NULL; + + if ( !iContactsDb ) + { + User::Leave( KErrNotReady ); + } + + iDataBuffer = CBufFlat::NewL( KDefaultBufferSize ); + iReaderPosition = 0; + + CContactItem* item = iContactsDb->ReadContactLC( aUid ); + if ( item->Type() != KUidContactGroup ) + { + CleanupStack::PopAndDestroy( item ); + User::Leave( KErrNotFound ); + } + + iConverter->ImportDbItemL( *static_cast(item) ); + + CleanupStack::PopAndDestroy( item ); + + iConverter->ExportVCardL( *iDataBuffer ); + iConverter->ResetL(); + aSize = iDataBuffer->Size(); + + // Set mime type to correct one + AssignString( aMimeType, KContactsGrpItemMimeType ); + AssignString( aMimeVer, KContactsGrpItemMimeVersion ); + + User::RequestComplete( iCallerStatus, KErrNone ); + iCurrentState = EContactGrpOpening; + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCreateItemL +// Create new item to the message store. +// Return the id number of the newly created item +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCreateItemL( + TSmlDbItemUid& aUid, + TInt aSize, + TSmlDbItemUid /*aParent*/, + const TDesC8& /*aMimeType*/, + const TDesC8& /*aMimeVer*/, + TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Ensure that we're in a proper state + if ( iCurrentState != EOpenAndWaiting ) + { + LOGGER_WRITE_1( "Warning: Unexpected current state: %d", iCurrentState ); + } + + if ( !iContactsDb ) + { + LOGGER_WRITE( "iContactsDb not ready" ); + User::Leave( KErrNotReady ); + } + + // Ensure that we've got enough disk space for the item + if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aSize, EDriveC ) ) + { + User::RequestComplete( iCallerStatus, KErrDiskFull ); + LOGGER_WRITE( "SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull." ); + return; + } + + // item uid is updated when groups is saved to db. + iCurrentItem = &aUid; + // Save current operation-state so we know what operation + // is needed to do on DoCommitItemL + iCurrentState = EContactGrpCreating; + + delete iDataBuffer; + iDataBuffer = NULL; + iDataBuffer = CBufFlat::NewL( KDefaultBufferSize ); + iWriterPosition = 0; + + User::RequestComplete( iCallerStatus, KErrNone ); + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoReplaceItemL +// Begin the replace operation, ensure that the item really exists +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoReplaceItemL( + TSmlDbItemUid aUid, + TInt aSize, + TSmlDbItemUid /*aParent*/, + TBool /*aFieldChange*/, + TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1("aUid: %d", aUid); + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + if ( !iContactsDb ) + { + LOGGER_WRITE( "iContactsDb not ready" ); + User::Leave( KErrNotReady ); + } + + // Ensure that we've got enough disk space for the item + if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aSize, EDriveC ) ) + { + User::RequestComplete( iCallerStatus, KErrDiskFull ); + LOGGER_WRITE( "SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull." ); + return; + } + + // check that item exist and is Group-type + CContactItem* item = iContactsDb->ReadContactLC( aUid ); + if ( item->Type() != KUidContactGroup ) + { + CleanupStack::PopAndDestroy( item ); + User::Leave( KErrNotFound ); + } + CleanupStack::PopAndDestroy( item ); + + // init databuffer + delete iDataBuffer; + iDataBuffer = NULL; + iDataBuffer = CBufFlat::NewL( KDefaultBufferSize ); + iWriterPosition = 0; + + // Save current item, so we know what item needs to be replaced + iItemToBeReplaced = aUid; + // Save current operation-state so we know what operation + // is needed to do on DoCommitItemL + iCurrentState = EContactGrpUpdating; + + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoReadItemL +// Read specified amount of data from the temporary buffer +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoReadItemL( TDes8& aBuffer ) + { + TRACE_FUNC_ENTRY; + + if ( iCurrentState != EContactGrpOpening || !iDataBuffer ) + { + LOGGER_WRITE_1( "Unexpected state %d", iCurrentState ); + User::Leave( KErrNotReady ); + } + + if ( iReaderPosition == KDataBufferNotReady ) + { + LOGGER_WRITE( "No data to read" ); + User::Leave( KErrEof ); + } + + // Thiw is how much data there is left in the buffer + TInt left = iDataBuffer->Size() - iReaderPosition; + + if ( left > 0 ) + { + // This is how much there's space in the destination buffer + TInt destSize = aBuffer.MaxSize(); + + // This is how much we can read + TInt toRead = destSize < left ? destSize : left; + + // Read the data from the buffer, then update the position + iDataBuffer->Read( iReaderPosition, aBuffer, toRead ); + iReaderPosition += toRead; + } + else + { + iReaderPosition = KDataBufferNotReady; + LOGGER_WRITE( "No data to read" ); + User::Leave( KErrEof ); + } + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoWriteItemL +// Write specified amount of data to the temporary buffer +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoWriteItemL( const TDesC8& aData ) + { + TRACE_FUNC_ENTRY; + + if ( iCurrentState != EContactGrpCreating && iCurrentState != EContactGrpUpdating ) + { + LOGGER_WRITE_1( "Unexpected current state: %d", iCurrentState ); + User::Leave( KErrNotReady ); + } + + // Calculate total size + TInt totalSize = aData.Size() + iDataBuffer->Size(); + + // Ensure that we've got enough disk space for the item + if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, totalSize, EDriveC ) ) + { + User::RequestComplete( iCallerStatus, KErrDiskFull ); + LOGGER_WRITE("SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull."); + return; + } + + // Add data to buffer + iDataBuffer->InsertL( iWriterPosition, aData ); + iWriterPosition += aData.Size(); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCommitItemL +// Commits item from temporary buffer to the message store +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCommitItemL( TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Check that we're in proper state + if ( iCurrentState != EContactGrpCreating && iCurrentState != EContactGrpUpdating ) + { + User::RequestComplete( iCallerStatus, KErrNotReady ); + LOGGER_WRITE_1( "Unexpected current state: %d", iCurrentState ); + return; + } + + if ( iDataBuffer->Size() <= 0 ) + { + User::RequestComplete( iCallerStatus, KErrNotReady ); + LOGGER_WRITE_1( "Data buffer has no data (%d)", iDataBuffer->Size() ); + return; + } + + if ( !iContactsDb ) + { + LOGGER_WRITE( "iContactsDb not ready" ); + User::Leave( KErrNotReady ); + } + + iDataBuffer->Compress(); + iConverter->ImportVCardL( iDataBuffer->Ptr(0) ); + + const CContactIdArray& contactArr = iConverter->ItemsContained(); + + if ( iCurrentState == EContactGrpCreating ) + { + *iCurrentItem = CreateNewGroupL( iConverter->GroupLabel(), contactArr ); + } + else + { + ReplaceGroupL( iItemToBeReplaced, iConverter->GroupLabel(), contactArr); + } + iConverter->ResetL(); + + // Destroy buffer + delete iDataBuffer; + iDataBuffer = NULL; + iWriterPosition = 0; + + // Restore state and signal we're done + iCurrentState = EOpenAndWaiting; + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::CreateNewGroupL +// Creates new groupd to db. +// ----------------------------------------------------------------------------- +TContactItemId CContactsGrpDataStore::CreateNewGroupL( const TDesC& aLabel, + const CContactIdArray& aContactArray ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1("aLabel: %S", &aLabel ); + + // Check for duplicates + if ( GroupNameExistsL( aLabel ) ) + { + LOGGER_WRITE("Duplicate group name found, leave KErrAlreadyExists"); + User::Leave( KErrAlreadyExists ); + } + + CContactGroup* group = + static_cast + (iContactsDb->CreateContactGroupLC( aLabel )); + + // Create new contact group + + TContactItemId groupId = group->Id(); + LOGGER_WRITE_1("new group id: %d", groupId); + + TBool partiallyUpdated(EFalse); + TTime lastMod; + lastMod.UniversalTime(); + TInt err(KErrNone); + for ( TInt i=0; iAddContactToGroupL( aContactArray[i], groupId )); + if ( err ) + { + LOGGER_WRITE_2("Warning: AddContactToGroupL, contact: %d, err: %d", aContactArray[i], err); + partiallyUpdated = ETrue; + } + else + { + // If succesfully added, update contact timestamp so contact sync sees those + // contacts as changed ones. + UpdateContactLastMod( aContactArray[i], lastMod ); + } + } + + CleanupStack::PopAndDestroy( group ); + group = NULL; + + // if item was only partially updated, do not register to snapshot + // --> on next sync item is marked as updated + if ( !partiallyUpdated ) + { + // reload group + group = static_cast + (iContactsDb->ReadContactLC( groupId )); + // Inform ChangeFinder of new item + TSnapshotItem snapshotItem( groupId ); + snapshotItem.CreateHashL( *group ); + iChangeFinder->ItemAddedL( snapshotItem ); + + CleanupStack::PopAndDestroy( group ); + } + + + + TRACE_FUNC_EXIT; + return groupId; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::ReplaceGroupL +// Replaces existing group on db +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::ReplaceGroupL( TContactItemId aGroupId, const TDesC& aLabel, + const CContactIdArray& aContactArray ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1("aGroupId: %d", aGroupId); + LOGGER_WRITE_1("aLabel: %S", &aLabel); + TBool partiallyUpdated(EFalse); + // check that item exist and is Group-type + // The LX suffix means that the lock record of the contact item is left on the cleanup stack. + CContactItem* item = iContactsDb->OpenContactLX( aGroupId ); + CleanupStack::PushL( item ); + if ( item->Type() != KUidContactGroup ) + { + LOGGER_WRITE("Type was not KUidContactGroup, leaving KErrNotFound"); + User::Leave( KErrNotFound ); + } + + // cast item to CContactGroup type + CContactGroup* groupItem = static_cast(item); + TContactItemId groupId = groupItem->Id(); + + TBool labelUpdated(EFalse); + // Check is group label changed + if ( groupItem->HasItemLabelField() ) + { + TPtrC dbLabel = groupItem->GetGroupLabelL(); + if ( aLabel.Compare( dbLabel ) != 0 ) + { + // didn't match, update label. + // Check that new name does not already exist + if ( GroupNameExistsL( aLabel ) ) + { + LOGGER_WRITE("Name already exists, leave KErrAlreadyExists"); + User::Leave( KErrAlreadyExists ); + } + LOGGER_WRITE("Update label" ); + groupItem->SetGroupLabelL( aLabel ); + iContactsDb->CommitContactL( *item ); + labelUpdated = ETrue; + } + } + TTime lastMod; + lastMod.UniversalTime(); + // Update joined contacts + TInt err(KErrNone); + // add new items to current group + for ( TInt i=0; iContainsItem( newItem ); + if ( !founded ) + { + // item does not exist on current group, add it. + LOGGER_WRITE_1("Add contact: %d", newItem) + TRAP( err, iContactsDb->AddContactToGroupL( newItem, groupId )); + if ( err ) + { + LOGGER_WRITE_2("Warning: Could not add contact: %d, err: %d", newItem, err); + partiallyUpdated = ETrue; + } + else + { + // Update contact timestamp + UpdateContactLastMod( newItem, lastMod ); + } + } + else + { + LOGGER_WRITE_1("contact( %d ) already exist on group, no need to update", newItem) + } + } + + // remove all missing items from group + CContactIdArray* oldIdArr = groupItem->ItemsContainedLC(); + if ( oldIdArr ) + { + for ( TInt i=0; iCount(); i++ ) + { + TContactItemId oldItem = (*oldIdArr)[i]; + TInt err = aContactArray.Find( oldItem ); + if ( err == KErrNotFound ) + { + LOGGER_WRITE_1("Remove contact: %d", oldItem ); + // old item was not found from new item list, remove it. + TRAP( err, iContactsDb->RemoveContactFromGroupL(oldItem, groupId)); + if ( err ) + { + LOGGER_WRITE_2("Warning: Could not remove contact: %d, err: ",oldItem, err ); + partiallyUpdated = ETrue; + } + else + { + UpdateContactLastMod( oldItem, lastMod ); + } + } + // Update all contacts in group if label is changed + else if ( labelUpdated ) + { + UpdateContactLastMod( oldItem, lastMod ); + } + } + } + CleanupStack::PopAndDestroy( oldIdArr ); + CleanupStack::PopAndDestroy( 2 ); // item, lock object + + // if item was only partially updated, do not register to snapshot + // --> on next sync item is marked as updated + if ( !partiallyUpdated ) + { + // reload group + groupItem = NULL; + groupItem = static_cast + (iContactsDb->ReadContactLC( groupId )); + // Inform ChangeFinder of updated item + TSnapshotItem snapshotItem( groupId ); + snapshotItem.CreateHashL( *groupItem ); + iChangeFinder->ItemUpdatedL( snapshotItem ); + + CleanupStack::PopAndDestroy( groupItem ); + } + + TRACE_FUNC_EXIT; + } +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCloseItem +// Closes open item in the data store +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCloseItem() + { + TRACE_FUNC_ENTRY; + // Reset read buffer + iReaderPosition = KDataBufferNotReady; + delete iDataBuffer; + iDataBuffer = NULL; + + // Make sure that we're opened an item + if ( iCurrentState != EContactGrpOpening ) + { + LOGGER_WRITE_1( "WARNING: Invalid state %d.", iCurrentState ); + } + // Start to wait for the next operation + iCurrentState = EOpenAndWaiting; + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoMoveItemL +// Not supported. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoMoveItemL( TSmlDbItemUid /*aUid*/, + TSmlDbItemUid /*aNewParent*/, TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Restore state and signal we're done + User::RequestComplete( iCallerStatus, KErrNotSupported ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoDeleteItemL +// Removes item from the message store +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoDeleteItemL( TSmlDbItemUid aUid, TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "aUid: %d", aUid ); + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Check that we're in proper state + if ( iCurrentState != EOpenAndWaiting ) + { + LOGGER_WRITE_1("CContactsGrpDataStore::DoDeleteItemL, Incorrect state: %d", iCurrentState); + } + + if ( !iContactsDb ) + { + LOGGER_WRITE( "iContactsDb not ready" ); + User::Leave( KErrNotReady ); + } + + // check that item type is ContactGroup + CContactItem* item = iContactsDb->ReadContactLC( aUid ); + if ( item->Type() != KUidContactGroup ) + { + LOGGER_WRITE("Item was not ContactGroup"); + CleanupStack::PopAndDestroy( item ); + User::Leave( KErrNotFound ); + } + + // Update all contacts in group + CContactGroup* groupItem = static_cast(item); + CContactIdArray* contactArr = groupItem->ItemsContainedLC(); + if ( contactArr ) + { + TTime lastMod; + lastMod.UniversalTime(); + for ( TInt i=0; iCount(); i++ ) + { + TContactItemId contactId = (*contactArr)[i]; + UpdateContactLastMod( contactId, lastMod ); + } + } + CleanupStack::PopAndDestroy( contactArr ); + CleanupStack::PopAndDestroy( item ); + + // delete group + iContactsDb->DeleteContactL( aUid ); + + TSnapshotItem snapshotItem( aUid ); + iChangeFinder->ItemDeleted( snapshotItem ); + + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoSoftDeleteItemL +// Soft delete isn't supported. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoSoftDeleteItemL(TSmlDbItemUid /*aUid*/, TRequestStatus& aStatus) + { + TRACE_FUNC_ENTRY; + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + User::RequestComplete(iCallerStatus, KErrNotSupported); + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoDeleteAllItemsL +// Deletes all items in the standard folders of bookmark store +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoDeleteAllItemsL( TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + if ( !iContactsDb ) + { + LOGGER_WRITE( "iContactsDb is not opened, Leaving KErrNotReady"); + User::Leave( KErrNotReady ); + } + + CContactIdArray* groups = iContactsDb->GetGroupIdListL(); + if ( groups ) + { + CleanupStack::PushL( groups ); + TTime lastMod; + lastMod.UniversalTime(); + + // update all contacts in all groups + for ( TInt i=0; iCount(); i++ ) + { + // check that item type is ContactGroup + CContactItem* item = iContactsDb->ReadContactLC( (*groups)[i] ); + + // Update all contacts in group + CContactGroup* groupItem = static_cast(item); + CContactIdArray* contactArr = groupItem->ItemsContainedLC(); + if ( contactArr ) + { + for ( TInt i=0; iCount(); i++ ) + { + TContactItemId contactId = (*contactArr)[i]; + UpdateContactLastMod( contactId, lastMod ); + } + } + CleanupStack::PopAndDestroy( contactArr ); + CleanupStack::PopAndDestroy( item ); + } + + // delete all groups + iContactsDb->DeleteContactsL( *groups ); + CleanupStack::PopAndDestroy( groups ); + } + + // Reset the whole change finder + iChangeFinder->ResetL(); + + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoHasSyncHistory +// This method returns ETrue if Data Store has history information. +// Slow-sync will be used if Data Store does not have history information. +// ----------------------------------------------------------------------------- +TBool CContactsGrpDataStore::DoHasSyncHistory() const + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "iHasHistory: %d", iHasHistory ); + TRACE_FUNC_EXIT; + return iHasHistory; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoAddedItems +// This method returns UIDs of added items. Those items are added after previous +// synchronization with current synchronization relationship. +// ----------------------------------------------------------------------------- +const MSmlDataItemUidSet& CContactsGrpDataStore::DoAddedItems() const + { + TRACE_FUNC_ENTRY; + + // Clear new-items array + iNewItems->Reset(); + if ( !iChangeFinder ) + { + LOGGER_WRITE( "WARNING: Not ready, returns empty item list" ); + return *iNewItems; + } + + // Search for new items + TRAPD( error, iChangeFinder->FindNewItemsL( *iNewItems ) ) + if ( error != KErrNone ) + { + LOGGER_WRITE_1( "CContactsGrpDataStore::DoAddedItems, iChangeFinder->FindNewItemsL leaved with %d.", error ); + } + + LOGGER_WRITE_1( "New item count: %d.", iNewItems->ItemCount() ); + TRACE_FUNC_EXIT; + + return *iNewItems; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoDeletedItems +// Returns ids of items, which are deleted after previous synchronization +// ----------------------------------------------------------------------------- +const MSmlDataItemUidSet& CContactsGrpDataStore::DoDeletedItems() const + { + TRACE_FUNC_ENTRY; + + // Clear deleted-items array + iDeletedItems->Reset(); + if ( !iChangeFinder ) + { + LOGGER_WRITE( "WARNING: Not ready, returns empty item list" ); + return *iDeletedItems; + } + + // Search for deleted items + TRAPD( error, iChangeFinder->FindDeletedItemsL( *iDeletedItems ) ); + if ( error != KErrNone ) + { + LOGGER_WRITE_1( "CContactsGrpDataStore::DoDeletedItems, iChangeFinder->FindDeletedItemsL leaved with %d.", error ); + } + + LOGGER_WRITE_1( "Deleted item count: %d.", iDeletedItems->ItemCount() ); + TRACE_FUNC_EXIT; + return *iDeletedItems; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoSoftDeletedItems +// Not directly supported, empty list returned +// ----------------------------------------------------------------------------- +const MSmlDataItemUidSet& CContactsGrpDataStore::DoSoftDeletedItems() const + { + TRACE_FUNC; + // Return empty array as a result + return *iEmptyList; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoModifiedItems +// Finds all modified items in the data store +// ----------------------------------------------------------------------------- +const MSmlDataItemUidSet& CContactsGrpDataStore::DoModifiedItems() const + { + TRACE_FUNC_ENTRY; + + // Clear updated-items array + iUpdatedItems->Reset(); + if ( !iChangeFinder ) + { + LOGGER_WRITE( "WARNING: Not ready, returns empty item list" ); + return *iUpdatedItems; + } + + // Search for updated items + TRAPD( error, iChangeFinder->FindChangedItemsL( *iUpdatedItems ) ) + if ( error != KErrNone ) + { + LOGGER_WRITE_1( "CContactsGrpDataStore::DoModifiedItems, iChangeFinder->FindChangedItemsL leaved with %d.", error ); + } + + LOGGER_WRITE_1( "Modified item count: %d.", iUpdatedItems->ItemCount() ); + TRACE_FUNC_EXIT; + return *iUpdatedItems; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoMovedItems +// Finds all moved items in the data store +// ----------------------------------------------------------------------------- +const MSmlDataItemUidSet& CContactsGrpDataStore::DoMovedItems() const + { + TRACE_FUNC; + // Moved items are not supported + // Return empty array as a result + return *iEmptyList; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoResetChangeInfoL +// Resets change history in the data store. All content is considered +// new in the data store point of view. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoResetChangeInfoL( TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Check that we're in proper state + if ( iCurrentState != EOpenAndWaiting ) + { + LOGGER_WRITE_1( "CContactsGrpDataStore::DoResetChangeInfoL, invalid state %d.", iCurrentState ); + } + + // Reset change info in ChangeFinder + iChangeFinder->ResetL(); + iHasHistory = EFalse; + + // Signal we're done + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCommitChangeInfoL +// Commits change info. These items are no longer reported, when change +// information is being queried. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus, + const MSmlDataItemUidSet& aItems ) + { + TRACE_FUNC_ENTRY; + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Ensure that we're in a proper state + if ( iCurrentState != EOpenAndWaiting ) + { + LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState ); + } + + // Notify ChangeFinder + LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, item count %d.", aItems.ItemCount() ); + iChangeFinder->CommitChangesL( aItems ); + iHasHistory = ETrue; + + // Signal we're done + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::DoCommitChangeInfoL +// Commits change info. There is no more nothing to report when change +// information is being queried. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus ) + { + TRACE_FUNC_ENTRY; + + iCallerStatus = &aStatus; + *iCallerStatus = KRequestPending; + + // Ensure that we're in a proper state + if ( iCurrentState != EOpenAndWaiting ) + { + LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState ); + } + + // Notify ChangeFinder + iChangeFinder->CommitChangesL(); + iHasHistory = ETrue; + + // Signal we're done + User::RequestComplete( iCallerStatus, KErrNone ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::RegisterSnapshotL +// Sets Changefinder to compare against current message store content +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::RegisterSnapshotL() + { + TRACE_FUNC_ENTRY; + + CArrayFixSeg* snapshot = + new ( ELeave ) CArrayFixSeg( KSnapshotGranularity ); + CleanupStack::PushL( snapshot ); + + // Add everything to snapshot + + // GetGroupIdListL returns NULL if there are no groups in the database. + CContactIdArray* groups = iContactsDb->GetGroupIdListL(); + if ( groups ) + { + TKeyArrayFix key( iKey ); + CleanupStack::PushL( groups ); + for ( TInt i=0; iCount(); i++ ) + { + CContactItem* item = iContactsDb->ReadContactLC((*groups)[i]); + CContactGroup* groupItem = static_cast(item); + + LOGGER_WRITE_1( "Add group to snatshot, group id: %d",groupItem->Id() ); + + TSnapshotItem snapshotItem( groupItem->Id() ); + snapshotItem.CreateHashL( *groupItem ); + + snapshot->InsertIsqL( snapshotItem, key ); + + CleanupStack::PopAndDestroy( item ); + } + CleanupStack::PopAndDestroy( groups ); + } + + // Set new snapshot to compare against + iChangeFinder->SetNewSnapshot( snapshot ); + + // Changefinder takes ownership of the snapshot + CleanupStack::Pop( snapshot ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::AssignString +// Assigns data from one descriptor into another, truncates if too long +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::AssignString( TDes8& aDestination, const TDesC8& aSource ) + { + TInt targetLength = aSource.Length(); + if ( aDestination.MaxLength() < targetLength ) + { + targetLength = aDestination.MaxLength(); + } + + aDestination.Copy( aSource.Ptr(), targetLength ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::UpdateContactLastMod +// Updates contact item last modified time. Contact sync will see item as changed one. +// ----------------------------------------------------------------------------- +void CContactsGrpDataStore::UpdateContactLastMod( TContactItemId aContactId, + const TTime& aLastModified ) + { + // Ignore errors. + // Cannot update timestamp if contact is already open. However modified time + // is most likely still updated by UI. + TRAP_IGNORE( + // OpenContactLX() places the edit lock on the cleanup stack + CContactItem* contact = iContactsDb->OpenContactLX( aContactId ); + CleanupStack::PushL( contact ); + contact->SetLastModified( aLastModified ); + iContactsDb->CommitContactL( *contact ); + CleanupStack::PopAndDestroy( contact ); + CleanupStack::PopAndDestroy( 1 ); // lock object + ); + } + +// ----------------------------------------------------------------------------- +// CContactsGrpDataStore::GroupNameExistsL +// Checks does group name already exists. +// ----------------------------------------------------------------------------- +TBool CContactsGrpDataStore::GroupNameExistsL( const TDesC& aLabel ) + { + TBool duplicateFound( EFalse ); + // GetGroupIdListL returns NULL if there are no groups in the database. + CContactIdArray* groups = iContactsDb->GetGroupIdListL(); + if ( groups ) + { + CleanupStack::PushL( groups ); + for ( TInt i=0; iCount() && !duplicateFound; i++ ) + { + CContactItem* item = iContactsDb->ReadContactLC((*groups)[i]); + CContactGroup* groupItem = static_cast(item); + + if ( groupItem->HasItemLabelField() ) + { + TPtrC label = groupItem->GetGroupLabelL(); + if ( aLabel.Compare( label ) == 0 ) + { + duplicateFound = ETrue; + } + } + CleanupStack::PopAndDestroy( item ); + } + CleanupStack::PopAndDestroy( groups ); + } + + return duplicateFound; + }