diff -r 000000000000 -r e686773b3f54 pimprotocols/pbap/server/pbapfoldernodepb.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pimprotocols/pbap/server/pbapfoldernodepb.cpp Tue Feb 02 10:12:17 2010 +0200 @@ -0,0 +1,444 @@ +// Copyright (c) 2006-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: +// + +#include + +#include "pbapfoldernodepb.h" +#include "pbaprequest.h" +#include "pbapfolderclient.h" +#include "pbapexporter.h" +#include "pbaperrorreporter.h" +#include "pbapserver.h" +#include "pbapappheader.h" + +#include "btaccesshostlog.h" + +// constants +_LIT(KFolderPb, "pb"); + + +/*static*/ CFolderNodePb* CFolderNodePb::NewL(MVirtualFolderClient& aClient) + { + LOG_STATIC_FUNC + CFolderNodePb* self = new (ELeave) CFolderNodePb(aClient); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CFolderNodePb::CFolderNodePb(MVirtualFolderClient& aClient) +: CFolderNode(aClient, KFolderPb()) + { + LOG_FUNC + } + + +void CFolderNodePb::ConstructL() + { + LOG_FUNC + iHandleCache = CPbapPbHandleCache::NewL(iClient.ContactDB(), *this); + } + + +CFolderNodePb::~CFolderNodePb() + { + LOG_FUNC + + delete iHandleCache; + delete iAsyncExporter; + delete iSearchResults; + iCacheIndexesToExport.Close(); + } + + +void CFolderNodePb::CancelGet() + { + LOG_FUNC + // cancel asynchronous searching and sorting of contacts + iClient.ContactDbViews().CancelSearchAndSortRequest(); + + // cancel any asynchronous export operations and avoid iAsyncExporter is deleted + // by itself + CPbapPbExporter* tmpExporter = iAsyncExporter; + iAsyncExporter = NULL; + delete tmpExporter; + + // reset for next get request + Reset(); + } + +void CFolderNodePb::GetComplete() + { + LOG_FUNC + + if (iAsyncExporter) + { + delete iAsyncExporter; + iAsyncExporter = NULL; + } + } + + + void CFolderNodePb::Reset() + { + LOG_FUNC + + delete iSearchResults; + iSearchResults = NULL; + + delete iAppHeader; + iAppHeader = NULL; + + iCacheIndexesToExport.Reset(); + } + + +TInt CFolderNodePb::DoGetItem(TInt aHandle) + { + LOG_FUNC + // check that state has been reset after previous get request + if(iAsyncExporter) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists)); + return KErrAlreadyExists; + } + + TInt error = KErrNone; + TInt index = 0; + + if (iPhonebookChanged) + { + // the Pb phonebook has been modified since the last get request + // report precondition failed error to obex until next listing + // request completes (or new Pbap session initiated) + iClient.ErrorReporter().SendPreconditionFailedError(); + // error already reported above so don't update error + + delete iAppHeader; + iAppHeader = NULL; + } + // search for the requested handle in the cache + else if ((index = iHandleCache->FindHandle(aHandle)) != KErrNotFound) + { + // export the contact associated with this handle + TRAP(error, iAsyncExporter = CPbapPbItemExporter::NewL(iClient, *this, *iHandleCache, index, 1, iAppHeader->VCardVersion(), iAppHeader->Filter())); + } + else + { + // the handle does not exist must report not found error to Obex + iClient.ErrorReporter().SendNotFoundError(); + // error already reported above so don't update error + + delete iAppHeader; + iAppHeader = NULL; + } + return error; + } + + +TInt CFolderNodePb::DoGetListing() + { + LOG_FUNC + + TInt error = KErrNone; + if (iAppHeader->SearchValue().Length() || iAppHeader->Order() != SymbianPBAP::EIndexed) + { + // search or non-indexed sort requested + TRAP(error, SearchAndSortItemsL()); + } + else + { + // the order is indexed and no search was requested so export listing now + TRAP(error, ExportIndexSortedListingL()); + } + + if (error != KErrNone) + { + // error occurred so reset state ready for next get request + Reset(); + } + return error; + } + + +/** + Use database views to search and sort contacts +*/ +void CFolderNodePb::SearchAndSortItemsL() + { + LOG_FUNC + if(iSearchResults) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderSearchInProgress)); + User::Leave(KErrAlreadyExists); + } + + // The views only support alphabetical and phonetic sort orders so for index ordered + // searches use alphabetical order and then resort the results when search complete + SymbianPBAP::TOrder order = (iAppHeader->Order() == SymbianPBAP::EIndexed ? SymbianPBAP::EAlphabetical : iAppHeader->Order()); + + // create empty array to store results + iSearchResults = CContactIdArray::NewL(); + + // start the asynchronous search and sort + iClient.ContactDbViews().GetContactIdsMatchingCriteriaL(order, iAppHeader->SearchAttribute(), iAppHeader->SearchValue(), *iSearchResults, *this); + } + + +void CFolderNodePb::ExportIndexSortedListingL() + { + LOG_FUNC + // check that state has been reset after previous get request + if(iAsyncExporter) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists)); + User::Leave(KErrAlreadyExists); + } + + if(iCacheIndexesToExport.Count() != 0) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderCachedIndexes)); + User::Leave(KErrAlreadyExists); + } + + // build an array of indexes to cache items (handle/contact Id pairs) to export. + // Since the cache is ordered on handle the indexes match the ordering of the cache + + // determine the last handle to export and then start the append loop from iListStartOffset + TInt maxHandle = Min(iAppHeader->MaxListCount() + iAppHeader->ListStartOffset(), HandleCount()); + for (TInt index = iAppHeader->ListStartOffset(); index < maxHandle; ++index) + { + iCacheIndexesToExport.AppendL(index); + } + + // export array of cache items as listing + iAsyncExporter = CPbapPbListingExporter::NewL(iClient, *this, *iHandleCache, iCacheIndexesToExport); + } + + +TInt CFolderNodePb::DoGetCount() + { + LOG_FUNC + // check that state has been reset correctly + if(iAsyncExporter) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists)); + return KErrAlreadyExists; + } + + // export the Pb phonebook size. This is equal to the number of handles in the + // cache (assuming that 0.vcf is always counted even if no owner card is set) + TRAPD(error, iAsyncExporter = CPbapPbCountExporter::NewL(iClient, *this, HandleCount())); + return error; + } + + +TInt CFolderNodePb::DoGetFolder() + { + LOG_FUNC + // check that state has been reset correctly + if(iAsyncExporter) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists)); + return KErrAlreadyExists; + } + + // setup range of items to export + TInt count = 0; + if (iAppHeader->ListStartOffset() < HandleCount()) + { + // limit the number of items to export to the number of handles available from + // the offset + count = Min(iAppHeader->MaxListCount(), HandleCount() - iAppHeader->ListStartOffset()); + } + + // export the contacts associated with range of handle cache indexes (these + // are aleady sorted in increasing handle order) + TRAPD(error, iAsyncExporter = CPbapPbItemExporter::NewL(iClient, *this, *iHandleCache, iAppHeader->ListStartOffset(), count, iAppHeader->VCardVersion(), iAppHeader->Filter())); + return error; + } + + +void CFolderNodePb::HandleCacheChanged() + { + LOG_FUNC + + // the contact item a handle refers to has changed. All PullvCard requests will + // now be blocked until the next listing request (or session close) + iPhonebookChanged = ETrue; + // check to see if there is an export in progress + if (iSearchResults || iAsyncExporter) + + { + // cancel the export and report error according to the error reporting + // scheme for modified/deleted handles + CancelGet(); + } + } + + +void CFolderNodePb::HandleSearchAndSortComplete(TInt aError) + { + LOG_FUNC + + if (aError == KErrNone) + { + // sorting and searching completed successfully + TRAP(aError, ExportSearchAndSortResultsL()); + } + + if (aError != KErrNone) + { + // get request failed report error to obex and reset + iClient.ErrorReporter().SendServiceUnavailableError(); + Reset(); + } + } + + +void CFolderNodePb::ExportSearchAndSortResultsL() + { + LOG_FUNC + + // check that state has been reset after previous get request + if(iAsyncExporter) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderPbExportAlreadyExists)); + User::Leave(KErrAlreadyExists); + } + if(iCacheIndexesToExport.Count() != 0) + { + __ASSERT_DEBUG(EFalse, Panic(EVirtualFolderCachedIndexes)); + User::Leave(KErrAlreadyExists); + } + + TInt count = iSearchResults->Count(); + + // if the order is indexed then to get here means that a search value has been specified + if (iAppHeader->Order() == SymbianPBAP::EIndexed) + { + // sort the search result into index order + RArray sortedIndexes; + CleanupClosePushL(sortedIndexes); + for (TInt ii = 0; ii < count; ++ii) + { + TContactItemId contactId = (*iSearchResults)[ii]; + TInt index = iHandleCache->FindContactId(contactId); + if (index != KErrNotFound) + { + // insert index in order to match handle ordering of cache + sortedIndexes.InsertInOrderL(index); + } + } + + // results array no longer required + delete iSearchResults; + iSearchResults = NULL; + + // extract range of cache indexes to export + count = Min(iAppHeader->MaxListCount() + iAppHeader->ListStartOffset(), sortedIndexes.Count()); + for (TInt jj = iAppHeader->ListStartOffset(); jj < count; ++jj) + { + iCacheIndexesToExport.AppendL(sortedIndexes[jj]); + } + CleanupStack::PopAndDestroy(); // sortedIndexes + } + // to get here means it's a non-index order sort or search + else + { + // the search results array is sorted in the correct (alphabetic or + // phonetic) order. Build array of indexes of cache items which match the + // contact ids + TInt maxAndOffset = iAppHeader->MaxListCount() + iAppHeader->ListStartOffset(); + count = Min(count, maxAndOffset); + + // decide whether there is a OwnCard + TBool hasOwnCard = ETrue; + TInt ownCardItemIndex = iHandleCache->FindHandle(KOwnCardHandle); // always present + TContactItemId contactId = iHandleCache->ContactIdAtL(ownCardItemIndex); + if(contactId == KNullContactId) + { + hasOwnCard = EFalse; + } + + // fill the array iCacheIndexsToExport + for (TInt ii = iAppHeader->ListStartOffset(); ii < count; ++ii) + { + TContactItemId contactId = (*iSearchResults)[ii]; + TInt index = iHandleCache->FindContactId(contactId); + if (index!=KErrNotFound) + { + iCacheIndexesToExport.AppendL(index); + } + } + + // append an empty own card (0.vcf) when doing sorting on alphabetic or phonetic order + // search value should be empty when doing sorting + if(iAppHeader->SearchValue().Size() == 0 && !hasOwnCard) + { + // "count < maxAndOffset" means that + // "iSearchResults->Count()" < "iAppHeader->MaxListCount() + iAppHeader->ListStartOffset()" + // in which case, we need to append an empty OwnCard entry to iCacheIndexesToExport. + if(count < maxAndOffset) + { + iCacheIndexesToExport.AppendL(KOwnCardHandle); + } + } + // results array no longer required + delete iSearchResults; + iSearchResults = NULL; + } + + // export items associated with list of handle cache indexes + iAsyncExporter = CPbapPbListingExporter::NewL(iClient, *this, *iHandleCache, iCacheIndexesToExport); + } + + + void CFolderNodePb::HandleExportComplete(TInt aError) + { + LOG_FUNC + + if (aError) + { + if(iPhonebookChanged) + { + iClient.ErrorReporter().SendPreconditionFailedError(); + } + else + { + // error occured during export report service unavailable error to obex + iClient.ErrorReporter().SendServiceUnavailableError(); + } + } + + // a successful listing request allows the pb phonebook to be read from again + // after it has been modified + if (aError == KErrNone && iAppHeader->Operation() == EPullVCardListing) + { + iPhonebookChanged = EFalse; + } + + // get request complete so reset state ready for next one + Reset(); + } + + + TInt CFolderNodePb::HandleCount() + { + return iHandleCache->Count(); + } +