--- /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 <cntdb.h>
+
+#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<TInt> 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();
+ }
+