pimprotocols/pbap/server/pbapserver.cpp
changeset 0 e686773b3f54
child 6 e8e3147d53eb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pimprotocols/pbap/server/pbapserver.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,1430 @@
+// Copyright (c) 2007-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 <s32mem.h>
+#include <e32svr.h>
+
+// KMimeVCardExtension is defined in miutmsg.h however including miutmsg.h also includes
+// flogger.h and conflicts with our inclusion of commsdebug via btaccesshostlog.h
+//#include <miutmsg.h>
+_LIT(KMimeVCardExtension, ".vcf");
+
+#include <bt_sock.h>
+#include <obex.h>
+#include <obexbttransportinfo.h>
+
+#include <cntdb.h>
+
+#include "clientserver.h"
+#include "pbapserver.h"
+#include "pbapsession.h"
+#include "pbapappheader.h"
+#include "pbapfoldertree.h"
+#include "pbapfoldertelecom.h"
+#include "pbapfolderroot.h"
+#include "pbapfoldernodepb.h"
+#include "pbapfoldernodech.h"
+#include "pbapfoldersim.h"
+#include "pbapcontactdbviews.h"
+#include "pbapauthpasswordgetter.h"
+#include "pbapxml.h"
+#include "pbapserversecuritypolicy.h"
+#include "pbaplogeng.h"
+
+#include "btaccesshostlog.h"
+
+
+_LIT8(KPBAPLocalWho, "\x79\x61\x35\xf0\xf0\xc5\x11\xd8\x09\x66\x08\x00\x20\x0c\x9a\x66");
+
+
+const TInt KBufGranularity = 100;
+const TInt KContactsDbOpenAttempts = 10;
+const TInt KContactsDbOpenDelay = 100000; //0.1 secs
+
+//
+// TCleanupItem operations - CPbapServer::ReleaseObex()
+//
+void PbapServerReleaseObex(TAny* aPtr)
+	{
+	LOG_STATIC_FUNC
+	reinterpret_cast<CPbapServer*>(aPtr)->ReleaseObex();
+	}
+
+
+void CleanupPbapServerReleaseObexPushL(CPbapServer& aPbapServer)
+	{		 
+	LOG_STATIC_FUNC
+	TCleanupItem item(PbapServerReleaseObex, &aPbapServer);
+	CleanupStack::PushL(item);
+	}	
+
+
+//
+// CPbapServerShutdown
+//
+/*static*/ CPbapServerShutdown* CPbapServerShutdown::NewL()
+	{
+	LOG_STATIC_FUNC
+	CPbapServerShutdown* self=new(ELeave) CPbapServerShutdown;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+inline CPbapServerShutdown::CPbapServerShutdown()
+	: CTimer(EPriorityLow)
+	{
+	LOG_FUNC
+	CActiveScheduler::Add(this);
+	}
+
+inline void CPbapServerShutdown::ConstructL()
+	{
+	LOG_FUNC
+	CTimer::ConstructL();
+	}
+
+inline void CPbapServerShutdown::Start()
+	{
+	LOG_FUNC
+	After(KPbapServerShutdownDelay);
+	}
+
+void CPbapServerShutdown::RunL()
+	{
+	LOG_FUNC
+	CActiveScheduler::Stop();
+	}
+
+
+//
+// CPbapServer
+//
+/*static*/ CPbapServer* CPbapServer::NewL()
+	{
+	LOG_STATIC_FUNC
+	CPbapServer* self=new(ELeave) CPbapServer;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+	
+CPbapServer::CPbapServer()
+: CPolicyServer(EPriorityStandard, KPbapServerPolicy, ESharableSessions)
+	{
+	LOG_FUNC
+	}
+
+
+CPbapServer::~CPbapServer()
+	{
+	LOG_FUNC
+	
+	// delete the obex server first because its destructor may call
+	// functions which this class implements
+	ReleaseObex();
+	
+	if (iAsyncRestartObex)
+		{
+		iAsyncRestartObex->Cancel();
+		delete iAsyncRestartObex;
+		}
+
+	delete iPasswordGetter;
+	delete iPassword;
+	delete iOutboundObject;
+	delete iOutboundBuffer;
+	delete iVCardExporter;
+	delete iShutdown;
+	
+	// the database must still be open when closing contact views so always 
+	// delete the object owning the views before the database
+	delete iContactViews;
+
+	delete iFolderTree;
+	delete iLogWrapper;
+	delete iContacts;
+	iFs.Close();
+	iBufStreamer.Close();
+	iAppHeaderDetails.Close();
+	}
+	
+
+void CPbapServer::ConstructL()
+	{
+	LOG_FUNC
+
+	StartL(KPbapServerName);
+
+	// ensure that the server will exit even if the 1st client fails to connect
+	iShutdown = CPbapServerShutdown::NewL();
+	
+	// create buffers ready for first obex get request
+	iOutboundBuffer = CBufFlat::NewL(KBufGranularity);
+	iOutboundObject = CObexBufObject::NewL(iOutboundBuffer);
+
+	OpenContactsDbL();
+	
+	// create contact database view manager
+	iContactViews = CPbapContactDbViews::NewL(*iContacts);
+	
+	// if there is a problem with creating a logeng client then we will continue
+	// but without any call history folders
+	TRAP_IGNORE(CreateLogEngineClientL());
+	
+	// create vCard exporter
+	iVCardExporter = CPbapVCardExporterUtil::NewL(*iContacts, iLogWrapper);
+
+	// create the restart obex callback used when an obex error occurs
+ 	TCallBack callback(RestartObex, this);
+	iAsyncRestartObex = new(ELeave) CAsyncCallBack(callback, CActive::EPriorityStandard);
+
+	// start shutdown timer now
+	iShutdown->Start();
+	}
+
+	
+void CPbapServer::OpenContactsDbL()
+	{
+	LOG_FUNC
+	TInt attempts=0;
+	TInt error=KErrNone;
+	while (attempts < KContactsDbOpenAttempts)
+		{
+		TRAP(error, iContacts = CContactDatabase::OpenL());
+
+		if (error==KErrLocked || error==KErrInUse)
+			{
+			// db maybe locked due to race condition so try again
+			++attempts;
+			User::After(KContactsDbOpenDelay);
+			}
+		else
+			{
+			// success or not a db locked error so exit loop
+			break;
+			}
+		}
+	if (error)
+		{
+		LOG1(_L("Contacts database failed to open, error=%d"), error);
+		User::Leave(error);	
+		}
+	}
+
+
+void CPbapServer::CreateLogEngineClientL()
+	{
+	LOG_FUNC
+	User::LeaveIfError(iFs.Connect());
+	
+	// create access to call history information
+	iLogWrapper = CPbapLogWrapper::NewL(iFs);
+	}
+
+
+void CPbapServer::BuildVirtualFolderTreeL()
+	{
+	LOG_FUNC
+
+	// create the root folder
+	CVirtualFolders* rootFolder = CVirtualFolders::NewLC();
+
+	rootFolder->PlaceFolderL(CFolderRoot::NewL(*this)); //ownership passed
+	rootFolder->AttachSubtree(TelecomSubTreeL()); //ownership passed
+	
+#ifdef __INCLUDE_SIM1_FOLDERS__
+	// create the SIM1 folder below root			
+	CVirtualFolders* sim1Folder = CVirtualFolders::NewLC();
+
+	sim1Folder->PlaceFolderL(CFolderSIM1::NewL(*this)); //ownership passed
+	sim1Folder->AttachSubtree(SIMTelecomSubTreeL()); //ownership passed
+	rootFolder->AttachSubtree(sim1Folder); //ownership passed
+
+	CleanupStack::Pop(sim1Folder);
+#endif // __INCLUDE_SIM1_FOLDERS__
+		
+	CleanupStack::Pop(rootFolder);	
+	iFolderTree = rootFolder;
+	iCurrentFolder = iFolderTree;
+	}
+
+
+CVirtualFolders* CPbapServer::TelecomSubTreeL()
+	{
+	LOG_FUNC
+	
+	// create telecom folder
+	CVirtualFolders* telecomFolder = CVirtualFolders::NewLC();
+	telecomFolder->PlaceFolderL(CFolderTelecom::NewL(*this)); //ownership passed
+	
+	// create pb sub folder
+	CVirtualFolders* pbFolder = CVirtualFolders::NewLC();
+
+	pbFolder->PlaceFolderL(CFolderNodePb::NewL(*this)); //ownership passed
+	telecomFolder->AttachSubtree(pbFolder); //ownership passed
+
+	CleanupStack::Pop(pbFolder);
+	
+	// make sure that we successfully created a logeng client
+	if (iLogWrapper && (iLogWrapper->ClientAvailable()))
+		{
+		// create ich sub folder
+		CVirtualFolders* ichFolder = CVirtualFolders::NewLC();
+
+		ichFolder->PlaceFolderL(CFolderNodeIch::NewL(*this)); //ownership passed
+		telecomFolder->AttachSubtree(ichFolder); //ownership passed
+
+		CleanupStack::Pop(ichFolder);
+
+		//create och sub folder
+		CVirtualFolders* ochFolder = CVirtualFolders::NewLC();
+
+		ochFolder->PlaceFolderL(CFolderNodeOch::NewL(*this)); //ownership passed
+		telecomFolder->AttachSubtree(ochFolder); //ownership passed
+
+		CleanupStack::Pop(ochFolder);
+
+		// create mch sub folder
+		CVirtualFolders* mchFolder = CVirtualFolders::NewLC();
+
+		mchFolder->PlaceFolderL(CFolderNodeMch::NewL(*this)); //ownership passed
+		telecomFolder->AttachSubtree(mchFolder); //ownership passed
+
+		CleanupStack::Pop(mchFolder);
+
+		// create cch sub folder
+		CVirtualFolders* cchFolder = CVirtualFolders::NewLC();
+
+		cchFolder->PlaceFolderL(CFolderNodeCch::NewL(*this)); //ownership passed
+		telecomFolder->AttachSubtree(cchFolder); //ownership passed
+
+		CleanupStack::Pop(cchFolder);
+		}
+	
+	CleanupStack::Pop(telecomFolder);	
+	return telecomFolder;
+	}
+
+#ifdef __INCLUDE_SIM1_FOLDERS__
+CVirtualFolders* CPbapServer::SIMTelecomSubTreeL()
+	{
+	LOG_FUNC
+	
+	// create SIM1 telecom folder
+	CVirtualFolders* telecomFolder = CVirtualFolders::NewLC();
+
+	telecomFolder->PlaceFolderL(CFolderSIM1Telecom::NewL(*this)); //ownership passed
+
+	// create SIM1 pb sub folder
+	CVirtualFolders* pbFolder = CVirtualFolders::NewLC();
+
+	pbFolder->PlaceFolderL(CFolderSIM1NodePb::NewL(*this)); //ownership passed
+	telecomFolder->AttachSubtree(pbFolder); //ownership passed
+
+	CleanupStack::Pop(pbFolder);
+	
+	// create SIM1 ich sub folder
+	CVirtualFolders* ichFolder = CVirtualFolders::NewLC();
+
+	ichFolder->PlaceFolderL(CFolderSIM1NodeIch::NewL(*this)); //ownership passed
+	telecomFolder->AttachSubtree(ichFolder); //ownership passed
+
+	CleanupStack::Pop(ichFolder);
+	
+	// create SIM1 och sub folder
+	CVirtualFolders* ochFolder = CVirtualFolders::NewLC();
+
+	ochFolder->PlaceFolderL(CFolderSIM1NodeOch::NewL(*this)); //ownership passed
+	telecomFolder->AttachSubtree(ochFolder); //ownership passed
+
+	CleanupStack::Pop(ochFolder);
+	
+	// create SIM1 mch sub folder
+	CVirtualFolders* mchFolder = CVirtualFolders::NewLC();
+
+	mchFolder->PlaceFolderL(CFolderSIM1NodeMch::NewL(*this)); //ownership passed
+	telecomFolder->AttachSubtree(mchFolder); //ownership passed
+
+	CleanupStack::Pop(mchFolder);
+	
+	// create SIM1 cch sub folder
+	CVirtualFolders* cchFolder = CVirtualFolders::NewLC();
+
+	cchFolder->PlaceFolderL(CFolderSIM1NodeCch::NewL(*this)); //ownership passed
+	telecomFolder->AttachSubtree(cchFolder); //ownership passed
+
+	CleanupStack::Pop(cchFolder);
+	
+	CleanupStack::Pop(telecomFolder);
+	return telecomFolder;
+	}
+#endif // __INCLUDE_SIM1_FOLDERS__
+
+/*virtual*/ CContactDatabase& CPbapServer::ContactDB() const
+	{
+	LOG_FUNC
+	return *iContacts;
+	}
+
+
+/*virtual*/ CPbapContactDbViews& CPbapServer::ContactDbViews()
+	{
+	LOG_FUNC
+	return *iContactViews;
+	}
+
+/*virtual*/ CPbapLogWrapper& CPbapServer::LogClient() const
+	{
+	LOG_FUNC
+	return *iLogWrapper;
+	}
+	
+/*virtual*/ MPbapExporter& CPbapServer::Exporter()
+	{
+	LOG_FUNC
+	return *static_cast<MPbapExporter*>(this);
+	}
+
+
+/*virtual*/ MPbapErrorReporter& CPbapServer::ErrorReporter()
+	{
+	LOG_FUNC
+	return *static_cast<MPbapErrorReporter*>(this);
+	}
+
+
+/*virtual*/ void CPbapServer::StartExport()
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(!iExportInProgress, Panic(EPbapServerPanicExportInProgress));
+	
+	// open the obex stream ready for export
+	iBufStreamer.Open(*iOutboundBuffer);
+	
+	// set flag to indicate export started
+	iExportInProgress=ETrue;
+	}
+
+
+/*virtual*/ void CPbapServer::ExportListingBeginL()
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{	
+		// write the XML DTD header
+		PbapDTD::WriteBeginL(iBufStreamer);
+		}
+	}
+
+
+/*virtual*/ void CPbapServer::ExportListingEntryL(TInt aHandle, const TDesC& aName)
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{
+		// write listing entry to stream
+		PbapDTD::WriteListingEntryL(iBufStreamer, aHandle, aName);
+		}
+	}
+
+	
+/*virtual*/ void CPbapServer::ExportListingEndL()
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{
+		// write the XML DTD end tag
+		PbapDTD::WriteEndL(iBufStreamer);
+		}
+	}
+
+
+/*virtual*/ void CPbapServer::ExportCallHistoryL(const CLogEvent& aEvent, TVCardVersion aFormat, TUint64 aFilter)
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{
+		// convert log event to a vCard and add to stream
+		iVCardExporter->ExportCallHistoryL(aEvent, iBufStreamer, aFormat, aFilter);
+		}
+	}
+
+		
+/*virtual*/ void CPbapServer::ExportContactL(TContactItemId aContactId, TVCardVersion aFormat, TUint64 aFilter)
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{	
+		// convert contact to a vCard and add to stream
+		iVCardExporter->ExportContactL(aContactId, iBufStreamer, aFormat, aFilter);
+		}
+	}
+	
+
+/*virtual*/ void CPbapServer::ExportEmptyVCardL(TVCardVersion aFormat)
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{	
+		// create empty vCard and add to stream
+		iVCardExporter->ExportEmptyVCardL(iBufStreamer, aFormat);
+		}	
+	}
+
+
+/*virtual*/ void CPbapServer::ExportPhonebookSizeL(TInt aCount)
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{
+		// add phonebook size to obex header, total size is 4 bytes
+		TInt currentLength = iAppHeaderDetails.Length();
+		iAppHeaderDetails.ReAllocL(currentLength+4);
+		iAppHeaderDetails.SetMax();
+		
+		// add 1 byte tag id
+		iAppHeaderDetails[currentLength] = CPbapAppHeader::EPhonebookSize;
+		
+		// add length of arguments, 2 bytes
+		iAppHeaderDetails[currentLength+1] = 2;
+		
+		// add phonebook size, 2 bytes
+		BigEndian::Put16(&iAppHeaderDetails[currentLength+2], aCount);
+		}
+	}
+
+
+/*virtual*/ void CPbapServer::ExportNewMissedCallsL(TInt aCount)
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	if (iExportInProgress)
+		{
+		// add new missed call count to obex header, total size is 3 bytes
+		TInt currentLength = iAppHeaderDetails.Length();
+		iAppHeaderDetails.ReAllocL(currentLength+3);
+		iAppHeaderDetails.SetMax();
+		
+		// add 1 byte tag id
+		iAppHeaderDetails[currentLength] = CPbapAppHeader::ENewMissedCalls;
+
+		// add length of arguments, 1 byte
+		iAppHeaderDetails[currentLength+1] = 1;
+		
+		// add missed call count, 1 byte
+		iAppHeaderDetails[currentLength+2] = aCount;
+		}
+	}
+
+		
+/*virtual*/ void CPbapServer::FinaliseExportL()
+	{
+	LOG_FUNC
+	if(iExportInProgress == EFalse)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNotReadyForExport));
+		User::Leave(KErrNotReady);
+		}
+	// set flag to indicate no more writing to the stream
+	if (iExportInProgress)
+		{	
+		// close the stream
+		iBufStreamer.Close();
+	
+		// add header information
+		if (iAppHeaderDetails.Length())
+			{
+			iOutboundObject->SetAppParamL(iAppHeaderDetails);
+			}
+
+		// reset app header
+		iAppHeaderDetails.Zero();
+
+		// request active mode to improve sending if object is large enough
+		const TObexTransportInfo* transInfo = iObex->TransportInfo();
+		if (transInfo && (iOutboundObject->DataBuf()->Size() > transInfo->iTransmitMtu))
+			{
+			// ignore any error, the Open() may have failed
+			iPhysLinkAdaptor.ActivateActiveRequester();
+			}
+		
+		// send over obex
+		User::LeaveIfError(iObex->RequestIndicationCallback(iOutboundObject));
+		
+		iExportInProgress=EFalse;
+		}
+	}
+
+
+/*virtual*/ void CPbapServer::CancelExport()
+	{
+	LOG_FUNC
+	// set flag to indicate
+	iExportInProgress=EFalse;
+	iAppHeaderDetails.Zero();
+	
+	// cleanup the buffers used for the get requests
+	CleanupGetRequest();
+	}
+
+
+	
+/*virtual*/ void CPbapServer::SendServiceUnavailableError()
+	{
+	LOG_FUNC
+	ReportObexError(KErrGeneral);
+	}
+
+	
+/*virtual*/ void CPbapServer::SendPreconditionFailedError()
+	{
+	LOG_FUNC
+	ReportObexError(KErrArgument);		
+	}
+
+
+/*virtual*/ void CPbapServer::SendNotFoundError()
+	{
+	LOG_FUNC
+	ReportObexError(KErrNotFound);		
+	}
+
+
+/**
+ create new session
+ */		
+CSession2* CPbapServer::NewSessionL(const TVersion &aVersion, const RMessage2& /*aMessage*/) const
+	{
+	LOG_FUNC
+	TVersion v(KPbapServerMajorVersionNumber,KPbapServerMinorVersionNumber,KPbapServerBuildVersionNumber);
+	if (!User::QueryVersionSupported(v,aVersion))
+		{
+		User::Leave(KErrNotSupported);
+		}
+
+	// we only support one active session
+	if (iActiveSession)
+		{
+		User::Leave(KErrInUse);
+		}
+
+	return new(ELeave) CPbapSession();
+	}
+
+
+/**
+ a new session is being created Cancel the shutdown timer if it was running
+ */
+void CPbapServer::AddSession()
+	{
+	LOG_FUNC
+	iActiveSession = ETrue;
+	iShutdown->Cancel();
+	}
+
+
+/**
+ a session is being destroyed. Start the shutdown timer if it is the last session.
+ */
+void CPbapServer::DropSession()
+	{
+	LOG_FUNC
+	iActiveSession = EFalse;	
+	iShutdown->Start();
+	}
+	
+
+void CPbapServer::StartObexL()
+	{
+	LOG_FUNC
+
+	if (!iObex)
+		{
+		TBTServiceSecurity serv;
+		serv.SetUid(KPbapServerUid);
+
+		serv.SetAuthentication(ETrue); // note: this needs to be reconsidered in light of SSP.
+		serv.SetAuthorisation(ETrue);
+		serv.SetEncryption(ETrue);
+
+		// now set up Obex...
+		TObexBluetoothProtocolInfo info;
+		info.iTransport = KObexRfcommProtocol;
+		info.iAddr.SetPort(KRfcommPassiveAutoBind);
+		info.iAddr.SetSecurity(serv);
+
+		iObex = CObexServer::NewL(info);
+		iObex->SetLocalWho(KPBAPLocalWho);
+		iObex->SetTargetChecking(CObexServer::EAlways);	
+
+		// push self onto cleanup stack to ReleaseObex if a leave
+		CleanupPbapServerReleaseObexPushL(*this);
+		
+		// start accepting requests to obex server
+		TInt error = iObex->Start(this);
+		if (error != KErrNone)
+			{
+			LOG1(_L("Error %d starting Obex server"), error);
+			User::Leave(error);
+			}
+
+		if (iPassword)
+			{
+			// a password has been set so enable obex authentication
+			TRAP(error, iObex->SetChallengeL(*iPassword));
+			if (error != KErrNone)
+				{
+				LOG1(_L("Error %d setting authentication challenge"), error);
+				User::Leave(error);
+				}		
+			}
+	
+		// obex started succesfully, retrieve assigned RFCOMM channel
+		const TObexBtTransportInfo* transportInfo = static_cast<const TObexBtTransportInfo*>(iObex->TransportInfo());
+		TUint rfcommChannel = transportInfo->iAddr.Port();
+
+		// now the RFCOMM channel is known register the SDP record to allow bluetooth connections
+		TRAP(error, RegisterWithSdpL(rfcommChannel));
+		if (error != KErrNone)
+			{
+			LOG1(_L("Error %d registering SDP record"), error);
+			User::Leave(error);
+			}
+		
+		// enable client authentication challenge handling
+		iObex->SetCallBack(*this);		
+		CleanupStack::Pop(this);
+		}
+	}
+
+
+void CPbapServer::ReleaseObex()
+	{
+	LOG_FUNC
+	
+	// stop all connections to the obex server (we need to delete the server rather
+	// than just call Stop() otherwise the rfcomm channel will not be assigned
+	// a new value when starting it again)
+	delete iObex;
+	iObex = NULL;
+		
+	// delete the stored password
+	delete iPassword;
+	iPassword = NULL;
+	
+	// release the SDP record
+	ReleaseSdpRegistration();
+	}
+	
+/*static*/ TInt CPbapServer::RestartObex(TAny* aAny)
+	{
+	LOG_STATIC_FUNC
+	CPbapServer* self = static_cast<CPbapServer*>(aAny);
+	self->DoRestartObex();
+	return KErrNone;
+	}
+	
+void CPbapServer::DoRestartObex()
+	{
+	LOG_FUNC
+
+	// We need to keep the authentication password as this re-start was not initiated
+	// by the Client side application.
+	HBufC* currentPassword = iPassword;
+	iPassword = NULL;
+	ReleaseObex();
+	
+	iPassword = currentPassword;
+	TRAPD(err, StartObexL());
+
+	if (err != KErrNone)
+		{
+		LOG1(_L("Error %d restarting Obex server"), err);
+		
+		// nothing left to do, panic server to inform client
+		Panic(EPbapServerPanicUnrecoverableObexError);
+		}
+	}
+
+#ifdef _DEBUG
+void CPbapServer::ErrorIndication(TInt aError)
+#else
+void CPbapServer::ErrorIndication(TInt /*aError*/)
+#endif
+	{
+	LOG_FUNC
+	LOG1(_L("Obex Error Indication: %d"), aError);
+
+	// this is a fatal OBEX error so reset our state...
+	CleanupOnDisconnect();
+	
+	// ...and re-start the obex server (needs to be done async from this error indication callback)
+	iAsyncRestartObex->CallBack();
+	}
+
+
+void CPbapServer::TransportUpIndication()
+	{
+	LOG_FUNC
+	// Availablity is updated here in a deviation from the specification which 
+	// defines the Pbap session as existing from the time the OBEX connection is 
+	// established. Since OBEX can only maintain one connection over BT in the 
+	// current implemenation it is more practical for us to set our availablity now
+	// instead of waiting for the OBEX connection as the RFComm channel is already 
+	// closed effectively making our service unavailable
+	UpdateAvailability(EPbapUnavailable);
+	}
+
+	
+void CPbapServer::TransportDownIndication()
+	{
+	LOG_FUNC
+	// underlying transport has disconnected
+	UpdateAvailability(EPbapAvailable);
+	CleanupOnDisconnect();
+	}
+
+	
+void CPbapServer::ObexConnectIndication(const TObexConnectInfo& aRemoteInfo, const TDesC8& /*aInfo*/)
+	{
+	LOG_FUNC
+	LOG(_L("Connect packet target header:"));
+	LOGHEXDESC(aRemoteInfo.iTargetHeader);
+
+	if (aRemoteInfo.iTargetHeader!=KPBAPLocalWho)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicInvalidTargetHeader));
+		}
+
+	else
+		{
+		// build the virtual folder tree at the start of the session. Failure to build
+		// the folder tree is handled later with error respones to get/set path requests
+		TRAP_IGNORE(BuildVirtualFolderTreeL());
+		
+		// get remote address
+		TSockAddr sockAddr;
+		iObex->RemoteAddr(sockAddr);
+		TBTDevAddr devAddr = static_cast<TBTSockAddr>(sockAddr).BTAddr();
+		
+		// create a physical links adaptor
+		if (iSocketServ.Connect() == KErrNone)
+			{
+			// don't store the error from Open, any further sniff/active requests will not
+			// succeed but this will not stop any functionality
+			if (iPhysLinkAdaptor.Open(iSocketServ, devAddr) == KErrNone)
+				{
+				// request sniff straight away as we will request active mode when it is
+				// required
+				iPhysLinkAdaptor.ActivateSniffRequester();
+				}
+			}
+  		}
+	}
+
+
+void CPbapServer::ObexDisconnectIndication(const TDesC8& /*aInfo*/)
+	{
+	LOG_FUNC
+	CleanupOnDisconnect();
+	}
+
+
+void CPbapServer::PutRequestIndication()
+	{
+	LOG_FUNC
+	// Always return a Bad Request error in response to PUT requests
+	ReportObexError(KErrCorrupt);
+	}
+
+
+TInt CPbapServer::PutPacketIndication()
+	{
+	LOG_FUNC
+	// the remote device should have handled the PUT request error and not sent
+	// any data. If not then just respond to each packet with another error
+	return KErrIrObexRespForbidden;
+	}
+
+	
+void CPbapServer::PutCompleteIndication()
+	{
+	LOG_FUNC
+	// the remote device should have handled the PUT request error and not sent
+	// any data. If not then just respond with an error 
+	ReportObexError(KErrAccessDenied);
+	}
+
+
+// report an OBEX error based on a Symbian global error code
+void CPbapServer::ReportObexError(TInt aSymbianError)
+	{
+	TObexResponse obexErr = ERespSuccess;
+	
+	switch (aSymbianError)
+		{
+		case KErrAccessDenied: // the request is barred
+			obexErr = ERespForbidden;
+			break;
+			
+		case KErrNotFound: // the requested folder doesnot exist
+			obexErr = ERespNotFound;
+			break;
+
+		case KErrNotSupported: // the folder does not support get requests (e.g SIM repositories not implemented)
+			obexErr = ERespNotImplemented;
+			break;
+
+		case KErrArgument: // invalid parameter value in header
+			obexErr = ERespPreCondFailed;
+			break;
+
+		case KErrCorrupt: // badly formatted header
+			obexErr = ERespBadRequest;
+			break;
+
+		case KErrGeneral:
+		default: // another error stopped request completing (e.g out of memory)
+			obexErr = ERespServiceUnavailable;
+			break;			
+		}
+	iObex->RequestIndicationCallbackWithError(obexErr);
+	
+	// get request failed so clean up for the next one
+	CleanupGetRequest();
+	}
+		
+void CPbapServer::GetRequestIndication(CObexBaseObject* aRequiredObject)
+	{
+	LOG_FUNC
+	TRAPD(error, DoGetRequestIndicationL(aRequiredObject));
+	if (error != KErrNone)
+		{
+		ReportObexError(error);
+		}
+	}
+
+		
+void CPbapServer::DoGetRequestIndicationL(CObexBaseObject* aRequiredObject)
+	{
+	LOG_FUNC
+	CPbapAppHeader* appHeader = CPbapAppHeader::NewL();
+	CleanupStack::PushL(appHeader);
+	
+	// parse the application parameters
+	appHeader->ParseL(aRequiredObject->AppParam());
+		
+	// determine the operation from the object type and parsed application params	
+	TPbapOperation operation = appHeader->DeterminePBAPOperationL(aRequiredObject->Type());
+	
+	// we only allow one get operation at a time
+	if (iGetRequestFolder)
+		{
+		// get operation in progress
+		User::Leave(KErrInUse);
+		}
+		
+	// check name for .vcf extension
+	TPtrC name = aRequiredObject->Name();		
+	if (name.Right(KMimeVCardExtension().Length())==KMimeVCardExtension())
+		{
+		if (operation == EPullVCardListing)
+			{
+			// we found a .vcf extension but shouldn't have
+			User::Leave(KErrArgument);
+			}
+			
+		// trim extension
+		name.Set(name.Left(name.Length()-KMimeVCardExtension().Length()));
+		}
+	else if ((operation == EPullPhoneBook) || (operation == EPullVCard))
+		{
+		// we didn't find a .vcf extension but should have
+		User::Leave(KErrArgument);		
+		}
+	
+	// create a copy of the trimmed name so that it can be modified to remove the path
+	// information when we set iGetRequestFolder below
+	RBuf objectName;
+	objectName.CleanupClosePushL();
+	objectName.CreateL(name);
+
+	// navigate to folder (the path is always absolute for x-bt/phonebook type requests)
+	iGetRequestFolder = ResolvePath(objectName, appHeader->IsAbsolutePathOp());
+
+	// the remaining object name might be a file representation of a phonebook
+	// (e.g pb.vcf) if so it should have a matching folder representation. If this
+	// is true then route the get request to this folder
+	if (iGetRequestFolder && objectName.Length() && operation != EPullVCard)
+		{
+		LOG(_L("Re-routing Get request from file representation to folder representation"));
+		CVirtualFolders* subFolder = iGetRequestFolder->NavigateFolder(objectName);
+		if (subFolder)
+			{
+			iGetRequestFolder = subFolder;
+			objectName.Zero(); // no more information so set length of objectName to zero
+			}
+		}
+
+	// let the folder handle the get request. The request is completed asynchronously
+	// so it is the folders responsibilty to notify obex when processing is complete	
+	if (iGetRequestFolder)
+		{
+		// To supress false positive coverity error for alias and double_free from cleanup stack for appHeader.
+		// The function Get takes ownership of appHeader by aliasing and does its own destruction of it either immediately on error                    
+		// or when finnished with the object. 
+		// coverity[double_free]
+		TInt error = iGetRequestFolder->Folder().Get(objectName, appHeader);
+		if (error != KErrNone)
+			{
+			LOG1(_L("Error %d from issuing Get to folder object"), error);
+			ReportObexError(error);
+			}
+			
+		// ownership of appHeader passed so don't destroy it
+		CleanupStack::PopAndDestroy(); //objectName
+		CleanupStack::Pop(); //appHeader
+		}
+	else
+		{
+		CleanupStack::PopAndDestroy(2); //appHeader, objectName
+		ReportObexError(KErrNotFound);
+		}
+	}
+
+
+/**
+Return folder which request will be operating on, and modify passed in path
+descriptor to contain only the file name
+*/
+CVirtualFolders* CPbapServer::ResolvePath(TDes& aPath, TBool aAbsolute) const
+	{
+	LOG_FUNC
+	LOG2(_L("Resolving path %S, [absolute=%d]"), &aPath, aAbsolute);
+
+	const TChar KSlash('/');
+	TInt index = 0;
+	
+	// it is ok to have a NULL iFolderTree as we ignore any problems we had when
+	// building it so just return NULL
+	if (!iFolderTree)
+		{
+		return NULL;
+		}
+	
+	// if we have a iFolderTree then we must have a iCurrentFolder
+	if(!iCurrentFolder)
+		{
+		__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNoCurrentFolder));
+		return NULL;
+		}
+		
+	// start at root if absolute path or the current folder if relative path
+	CVirtualFolders* nextTree = aAbsolute ? iFolderTree : iCurrentFolder;
+
+	if (aPath.Length()>=1) // blank means dont change folder
+		{
+		if (aPath[index]==KSlash)
+			{
+			aPath.Delete(index,1);
+			nextTree = iFolderTree;
+			}
+
+		do
+			{
+			index = aPath.Locate(KSlash);
+			if (index!=KErrNotFound)
+				{
+				// try to enter subfolder
+				TPtrC folder(aPath.Left(index));
+				nextTree = nextTree->NavigateFolder(folder);
+				if (!nextTree)
+					{
+					// faulty path spec
+					nextTree = NULL;
+					break;
+					}
+				else
+					{
+					// trim off this bit of path AND the '/'!
+					aPath.Delete(0, index+1);
+					}
+				}
+			} while (index!=KErrNotFound);
+		}
+						
+	return nextTree;
+	}
+
+
+TInt CPbapServer::GetPacketIndication()
+	{
+	LOG_FUNC
+	// do nothing (the PSE doesnot support a UI with progress updates)
+	return KErrNone;
+	}
+
+	
+void CPbapServer::GetCompleteIndication()
+	{
+	LOG_FUNC
+
+	TInt err = iObex->RequestCompleteIndicationCallback(KErrNone);
+	__ASSERT_DEBUG(err==KErrNone, Panic(EPbapServerPanicUnexpectedError));
+
+	// prepare for next request
+	CleanupGetRequest();
+	}
+
+	
+void CPbapServer::SetPathIndication(const CObex::TSetPathInfo& aPathInfo, const TDesC8& /*aInfo*/)
+	{
+	LOG_FUNC
+	
+	TInt err = KErrNone;
+
+	// it is ok to have a NULL iFolderTree as we ignore any problems we had when
+	// building it
+	if (!iFolderTree)
+		{
+		LOG(_L("Folder tree not created"));
+		err = KErrNotFound;
+		}
+	else if (aPathInfo.Parent())
+		{
+		LOG(_L("Set folder to parent"));
+
+		// if we have a iFolderTree then we must have a iCurrentFolder
+		if(!iCurrentFolder)
+			{
+			__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNoCurrentFolder));
+			err = KErrNotFound;
+			}
+		else
+			{
+			// move to parent folder if it exists (it will not if already at root)
+			CVirtualFolders* folder = iCurrentFolder->ParentFolder();
+			if (folder)
+				{
+				iCurrentFolder = folder;
+				}
+			else
+				{
+				err = KErrNotFound;
+				}
+			}
+		}
+	else if (aPathInfo.iNamePresent)
+		{
+		if (aPathInfo.iName!=KNullDesC)
+			{
+			LOG1(_L("Set folder to child [%S]"), &aPathInfo.iName);
+
+			// if we have a iFolderTree then we must have a iCurrentFolder
+			if(!iCurrentFolder)
+				{
+				__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNoCurrentFolder));
+				err = KErrNotFound;
+				}
+			else
+				{
+				CVirtualFolders* folder = iCurrentFolder->NavigateFolder(aPathInfo.iName);
+				if (folder)
+					{
+					iCurrentFolder = folder;
+					}
+				else
+					{
+					err = KErrNotFound;
+					}
+				}
+			}
+		else
+			{
+			LOG(_L("Set folder to root"));
+
+			// if we have a iFolderTree then we must have a iCurrentFolder
+			if(!iCurrentFolder)
+				{
+				__ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNoCurrentFolder));
+				err = KErrNotFound;
+				}
+
+			iCurrentFolder = iFolderTree;
+			}
+		}
+	else
+		{
+		// this is not a request to change to the parent folder therefore we should have a name
+		// header but we don't, return error
+		LOG(_L("No Name header present"));
+		err = KErrArgument;
+		}
+	
+	// return any obex errors
+	if (err == KErrNotFound)
+		{
+		LOG(_L("Error folder does not exist"));
+		iObex->RequestCompleteIndicationCallback(ERespNotFound);	
+		}
+	else if (err == KErrArgument)
+		{
+		LOG(_L("Error invalid argument"));
+		iObex->RequestCompleteIndicationCallback(ERespPreCondFailed);	
+		}
+	else
+		{		
+		iObex->RequestCompleteIndicationCallback(KErrNone);	
+		}
+	}
+
+	
+void CPbapServer::AbortIndication()
+	{
+	LOG_FUNC
+	
+	// the get request was aborted during the obex transfer (i.e. after processing
+	// the request had completed). So just clean up the obex buffers
+	CleanupGetRequest();
+	}
+
+
+void CPbapServer::CancelIndicationCallback()
+	{
+	LOG_FUNC
+	// the get request was cancelled before sending any data.
+	// so cancel any ongoing asynchronous processing and cleanup obex buffers
+	if (iGetRequestFolder)
+		{
+		iGetRequestFolder->Folder().CancelGet();
+		}
+	
+	CleanupGetRequest();
+	}
+
+void CPbapServer::GetUserPasswordL(const TDesC& aRealm)
+	{
+	LOG_FUNC
+	LOG1(_L("Authentication challenge [Realm = %S]"), &aRealm);
+
+	delete iPasswordGetter;
+	iPasswordGetter = NULL;
+	iPasswordGetter = CPbapAuthPasswordGetter::NewL(*this);
+
+	// get the Bluetooth device address of the client making the authentication challenge
+	TSockAddr sockAddr;
+	iObex->RemoteAddr(sockAddr);
+	TBTDevAddr devAddr = static_cast<TBTSockAddr>(sockAddr).BTAddr();
+
+	// start async password request
+	iPasswordGetter->GetPassword(aRealm, devAddr);
+	}
+
+void CPbapServer::HandlePasswordRetrieved(TInt aError, const TDesC& aPassword)
+	{
+	LOG_FUNC
+	
+	if (aError == KErrNone)
+		{
+		// if we successfully got a password, pass it to obex
+		TRAP(aError, iObex->UserPasswordL(aPassword));
+		}
+		
+	if (aError != KErrNone)
+		{
+		LOG1(_L("Error %d creating response to authentication challenge"), aError);
+		// creating the challenge response has failed. Since there is no way to report
+		// the error to the obex server the only option is to force a reset of the
+		// connection state by restarting the server
+		iObex->Stop();
+		aError = iObex->Start(this);
+		if (aError != KErrNone)
+			{
+			LOG1(_L("Error %d restarting Obex server"), aError);
+			ReleaseObex();
+			
+			// nothing left to do, panic server to inform client
+			Panic(EPbapServerPanicUnrecoverableObexError);
+			}
+		}
+	}
+
+void CPbapServer::SetPasswordL(HBufC* aPassword)
+	{
+	LOG_FUNC
+	if (iObex)
+		{
+		// the obex server exists so set its challenge now
+		iObex->SetChallengeL(*aPassword);
+		}
+	// store the password (transfers ownership)
+	delete iPassword;
+	iPassword = aPassword;
+	}
+
+void CPbapServer::CleanupGetRequest()
+	{
+	LOG_FUNC
+
+	// request sniff mode again as get request finished, ignore errors
+	iPhysLinkAdaptor.ActivateSniffRequester();
+
+	// clean the exporter
+	if (iGetRequestFolder)
+		{
+		iGetRequestFolder->Folder().GetComplete();
+		}
+	
+	iGetRequestFolder = NULL;
+	iOutboundBuffer->Reset();
+	iOutboundObject->Reset();
+	iBufStreamer.Close();
+	}
+
+
+void CPbapServer::CleanupOnDisconnect()
+	{
+	LOG_FUNC
+	
+	CleanupGetRequest();
+	
+	// close physical links adaptor
+	iPhysLinkAdaptor.Close();
+	iSocketServ.Close();
+	
+	// destroy the virtual folder tree when the session ends
+	delete iFolderTree;
+	iFolderTree = NULL;
+	iCurrentFolder = NULL;
+		
+	// close contacts views to save memory (this will also cancel any current requests to sort and search views)
+ 	iContactViews->CloseAllViews();
+
+	// make sure the authentication notifier is cancelled by deleting its handler
+	delete iPasswordGetter;
+	iPasswordGetter = NULL;
+	}
+
+
+void Panic(TPbapServerPanicCode aPanic)
+	{
+	LOG_STATIC_FUNC
+	User::Panic(KPbapServerPanic, aPanic);
+	}
+
+
+/**
+ Perform all server initialisation, in particular creation of the
+ scheduler and server and then run the scheduler
+ */
+static void RunServerL()
+	{
+	LOG_STATIC_FUNC
+
+	// naming the server thread after the server helps to debug panics	
+	User::RenameThread(KPbapServerName); 
+			
+	// create and install the active scheduler we need
+	CActiveScheduler* scheduler=new(ELeave) CActiveScheduler;
+	CleanupStack::PushL(scheduler);
+	CActiveScheduler::Install(scheduler);
+
+	// create the server (leave it on the cleanup stack)
+	CPbapServer* server = CPbapServer::NewL();
+	CleanupStack::PushL(server);
+
+	// initialisation complete, now signal the client thread
+	RProcess::Rendezvous(KErrNone);
+
+	// ready to run
+	CActiveScheduler::Start();
+
+	// cleanup the server and scheduler
+	CleanupStack::PopAndDestroy(2, scheduler);
+	}
+
+
+/**
+ Server process entry-point.
+ @return  KErrNone or a standard Symbian error code.
+ */
+TInt E32Main()
+	{
+	__UHEAP_MARK;
+
+#ifdef __FLOG_ACTIVE
+	// connect to logger
+	(void)CBtLog::Connect();
+#endif
+
+	CTrapCleanup* cleanup=CTrapCleanup::New();
+	TInt r=KErrNoMemory;
+	if (cleanup)
+		{
+		TRAP(r, RunServerL());
+		delete cleanup;
+		}
+
+#ifdef __FLOG_ACTIVE
+	// close logger connection
+	CBtLog::Close();
+#endif
+
+	__UHEAP_MARKEND;
+	return r;
+	}