diff -r 000000000000 -r e686773b3f54 pimprotocols/pbap/server/pbapserver.cpp --- /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 +#include + +// 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 +_LIT(KMimeVCardExtension, ".vcf"); + +#include +#include +#include + +#include + +#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(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(this); + } + + +/*virtual*/ MPbapErrorReporter& CPbapServer::ErrorReporter() + { + LOG_FUNC + return *static_cast(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(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(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(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(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; + }