// Copyright (c) 2000-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 <bluetooth/logger.h>#include <e32svr.h>#include <e32uid.h>#include <btsdp.h>#include "DataEncoder.h"#include "EncoderVisitor.h"#include "sdputil.h"#include "reqhandler.h"#include "listener.h"#include "epocsvr.h"#include "ipcinternals.h"#include "attrvalueencoded.h"#include "epocsvr.inl"#include <f32file.h> // For RFs#ifdef __EPOC32__#include <c32comm.h>#endif#include "SdpServerSecurityPolicy.h"#ifdef __FLOG_ACTIVE_LIT8(KLogComponent, LOG_COMPONENT_SDP_SERVER);#endif#ifdef _DEBUGPANICCATEGORY("epocsvr");#endifGLDEF_D TInt sdp_debug_level = 5;void Panic(TSdpServerPanics aCode) { User::Panic(KSdpServerPanicName, aCode); }/** The SDP database subsession class has been removed from the SDP server but to maintain BC we still have to deal with subsession requests from the client. The constant below is used as the subsession handle for any new subsession requests from a client as we no longer maintain a real handle to a subsession.**/static const TInt KSdpDatabaseSubSessionHandle = 0x1A2B3C4D;inline CSdpServerShutdown::CSdpServerShutdown() :CTimer(-1) {CActiveScheduler::Add(this);}inline void CSdpServerShutdown::ConstructL() {CTimer::ConstructL();}inline void CSdpServerShutdown::Start() {After(KSdpServerShutdownDelay);}//void CSdpServerShutdown::RunL()/** Initiate server exit when the timer expires**/ { LOG_FUNC CActiveScheduler::Stop(); }CSdpServer::CSdpServer() : CPolicyServer(0, KSdpServerPolicy) { CONNECT_LOGGER LOG_FUNC }CSdpServer* CSdpServer::NewLC()/** Create the server object and leave on cleanup stack.**/ { LOG_STATIC_FUNC CSdpServer* self=new(ELeave) CSdpServer(); CleanupStack::PushL(self); self->ConstructL(); return self; }void CSdpServer::ConstructL() { LOG_FUNC StartL(KSdpServerName); iSdpDatabase = CSdpDatabase::NewL(); iUuidManager = CSdpUuidManager::NewL(*iSdpDatabase); TPckg<TUint> intBuf(0); iDbState = CSdpAttrValueUint::NewUintL(intBuf); // Create the buffer the correct size for storing a Uint TInt encodedSize = TElementEncoder::EncodedSize(iDbState->Type(), iDbState->DataSize()); iEncodeBuf.CreateL(encodedSize); TElementEncoder encoder(iEncodeBuf); iEncoderVisitor = CAttrEncoderVisitor::NewL(encoder); BuildRecordZeroL(); //Unencoded Database()->EncodeDbL(); /*If we get to here, database exists*/ User::LeaveIfError(iSocketServ.Connect()); iSdpListener = CSdpListener::NewL(iSocketServ, 4, *(Database())); //Ensure that the server will exit even if the 1st client fails to connect //Start shutdown timer only after all the heavy duty initialisation has been done. //Otherwise it could fire before any client had a chance to connect to the server iShutdown.ConstructL(); iShutdown.Start(); }CSdpServer::~CSdpServer() { LOG_FUNC delete iSdpListener; iSocketServ.Close(); delete iRecZero; delete iDbState; delete iEncoderVisitor; iEncodeBuf.Close(); delete iUuidManager; delete iSdpDatabase; CLOSE_LOGGER }CSdpDatabase* CSdpServer::Database() { LOG_FUNC return iSdpDatabase; }void CSdpServer::BuildRecordZeroL()/** Record 0 should be used when a server instance is created. note that attributes 2, 5 and 0x201 should be updated. also note only English, pas de Francais, keine Deutsch, non Espanol**/ { LOG_FUNC ASSERT_DEBUG(iSdpDatabase); TBuf8<2> attrId; TBuf8<4> val; iRecZero = CSdpServRecord::NewServerSideL(User::Identity()); // Set Attr 0 (Record handle) to 0 attrId.FillZ(2); val.FillZ(4); iRecZero->BuildUintL(attrId)->BuildUintL(val); // Set Attr 1 (service class list) to list with UUID = 0x1000 attrId[0] = 0x00; attrId[1] = 0x01; iRecZero->BuildUintL(attrId)->BuildDESL() ->StartListL() ->BuildUUIDL(TUUID(TUint16(KServiceDiscoveryServerServiceClassUUID))) ->EndListL(); // Set Attr 2 (service record state) to 0 attrId[0] = 0x00; attrId[1] = 0x02; val.FillZ(4); iRecZero->BuildUintL(attrId)->BuildUintL(val); // Set attr 4 (protocol list) to L2CAP, no RFCOMM, no OBEX attrId[0] = 0x00; attrId[1] = 0x04; val.FillZ(4); val[3] = 1; iRecZero->BuildUintL(attrId)->BuildDESL() ->StartListL() ->BuildDESL() ->StartListL() ->BuildUUIDL(TUUID(TUint16(0x0100))) // L2CAP ->EndListL() ->EndListL(); // Set Attr 5 (browse group list) to list with one UUID // 0x1000 (SDP server class) // this should be updated with other service classes when other services are added. attrId[0] = 0x00; attrId[1] = 5; iRecZero->BuildUintL(attrId)->BuildDESL() ->StartListL() ->BuildUUIDL(TUUID(TUint32(0x1000))) ->EndListL(); // Set Attr 0x006 (language base) attrId[0] = 0x00; attrId[1] = 0x06; TUint16 lang = 0x656e; TUint16 coding = 0x006a; TUint16 base = 0x0100; iRecZero->BuildUintL(attrId)->BuildDESL() ->StartListL() ->BuildUintL(TSdpIntBuf<TUint16>(lang)) // english ->BuildUintL(TSdpIntBuf<TUint16>(coding)) // UTF-8 ->BuildUintL(TSdpIntBuf<TUint16>(base)) // language base ->EndListL(); // Set Attr 0x007 (time to live) to 1200 (0x4B0) seconds (20 minutes) attrId[0] = 0x00; attrId[1] = 0x07; val.FillZ(4); val[2]=4; val[3]=0xb0; iRecZero->BuildUintL(attrId)->BuildUintL(val); // Set Attr 0x008 (availability) to 0xff - fully available - not in use attrId[0] = 0x00; attrId[1] = 0x08; TBuf8<1> val4; val4.FillZ(1); val4[0]=0xff; iRecZero->BuildUintL(attrId)->BuildUintL(val4); // Set Attr 0x100 (default Name) to string attrId[0] = 0x01; attrId[1] = 0; iRecZero->BuildUintL(attrId)->BuildStringL(_L8("SDP Server")); // Set Attr 0x101 (def. description) to string attrId[0] = 0x01; attrId[1] = 1; iRecZero->BuildUintL(attrId)->BuildStringL(_L8("Provides local service information for remote devices.")); // Set Attr 0x102 (def. provider) to Symbian attrId[0] = 0x01; attrId[1] = 2; iRecZero->BuildUintL(attrId)->BuildStringL(_L8("Symbian OS")); // Set Attr 0x200 (version number support) to list with 1.0 and 1.1 iRecZero->BuildUintL(TSdpIntBuf<TSdpAttributeID>(0x200)) ->BuildDESL() ->StartListL() ->BuildUintL(TSdpIntBuf<TUint16>(0x0100)) // 1.0 ->BuildUintL(TSdpIntBuf<TUint16>(0x0101)) // 1.1 ->EndListL(); // Set Attr 0x201 (service database state) to 0 attrId[0] = 0x02; attrId[1] = 0x01; val.FillZ(4); iRecZero->BuildUintL(attrId)->BuildUintL(val); // Add the record into the database Database()->AddRecord(iRecZero); }CSession2* CSdpServer::NewSessionL(const TVersion& /*aVersion*/) const { LOG_FUNC User::Leave(KErrNotSupported); return 0; }CSession2* CSdpServer::NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const { LOG_FUNC TVersion v(KSdpServerMajorVersionNumber,KSdpServerMinorVersionNumber,KSdpServerBuildVersionNumber); if (!User::QueryVersionSupported(v,aVersion)) { User::Leave(KErrNotSupported); } // make new session return new(ELeave) CSdpServSession(aMessage); }void CSdpServer::AddSession()/**A new session is being createdCancel the shutdown timer if it was running**/ { LOG_FUNC ++iSessionCount; if (iSessionCount > iMaxSessionCount) { iMaxSessionCount = iSessionCount; } iShutdown.Cancel(); }void CSdpServer::DropSession()/**A session is being destroyedStart the shutdown timer if it is the last session.**/ { __ASSERT_DEBUG(iSessionCount > 0, PanicServer(ESdpBadState)); if (--iSessionCount==0) { iShutdown.Start(); } }void CSdpServer::CheckAllowedL(const RMessage2& aMessage, const CSdpServRecord& aRecord) { LOG_FUNC if (!Allowed(aMessage, aRecord)) { User::Leave(KErrPermissionDenied); } }TBool CSdpServer::Allowed(const RMessage2& aMessage, const CSdpServRecord& aRecord) { LOG_FUNC _LIT_SECURITY_POLICY_S0(KSidPolicy, aRecord.ClientUid().iUid); //if the secure id of the record matches the secure id of the message sender if (KSidPolicy().CheckPolicy(aMessage)) { //allowed return ETrue; } //not allowed return EFalse; }CSdpServRecord* CSdpServer::FindRecordByHandle(const TSdpServRecordHandle aHandle)/**Returns a pointer to the first record object found with its attribute '0' set to the specified handle.If no such record object is found, this function return 0 (Null).**/ { LOG_FUNC for(TServRecordIter recIter(Database()->RecordIter()); recIter; recIter++) {// Iterate thru records in iDatabase if((*recIter).Handle()==aHandle) { //Destructor of a record removes/destroys all its //attributes and deques it from database list. return &(*recIter); } } return 0; }CSdpAttr* CSdpServer::FindAttributeByID(CSdpServRecord& aRecord, const TSdpAttributeID aAttrID)/**Returns a pointer to the attribute object found in the specified record that has the specified ID.If no such attribute object is found, this function return 0 (Null).**/ { LOG_FUNC for(TServAttrIter attrIter(aRecord.AttributeIter()); attrIter; attrIter++) {// Iterate thru attributes in record if((*attrIter).AttributeID()==aAttrID) { return &(*attrIter); } } return 0; }/**This function must be called whenever a service record is added or removed.The SDP server 0 service record has a ServerServiceDatabaseState attributewhich, if present, must be updated when a service record is added or removed.*/void CSdpServer::DatabaseStateChange() { LOG_FUNC //increment database state attribute in record zero if(iRecZero) { TUint state = iDbState->Uint(); TPckg<TUint> stateBuf(0); SdpUtil::PutUint(&stateBuf[0], ++state, sizeof(TUint)); iDbState->SetUintValue(stateBuf); // This encodes iDbState into the buffer provided at construction iEncodeBuf.SetLength(0); TRAPD(err, iEncoderVisitor->EncodeAttributeL(*iDbState)); // Attribute encoding can only fail if the attribute is of an unknown type // or the supplied buffer is too small. We have set the length of the // buffer to the correct length on creation so that will not fail. // We know iDbState is a CSdpAttrValueUint so cannot fail. __ASSERT_ALWAYS(!err, PanicServer(ESdpAttributeEncodingFailed)); CSdpAttr* attr = FindAttributeByID(*iRecZero, KSdpAttrIdSdpServerServiceDatabaseState); __ASSERT_ALWAYS(attr->Value().Type() == ETypeEncoded, PanicServer(ESdpStoredAttrValNotEncoded)); reinterpret_cast<CSdpAttrValueEncoded&>(attr->Value()).SetEncodedValue(iEncodeBuf); iRecZero->RecordStateChange(); iUuidManager->NotifySdpRecordChange(); } }CSdpServRecord* CSdpServer::CreateServiceRecordL(const RMessage2& aMessage)/**Creates a record on the global database. Returns a pointer to the new record.**/ { LOG_FUNC CSdpServRecord* record = CSdpServRecord::NewServerSideL(aMessage.Identity()); CleanupStack::PushL(record); //Build record handle attribute... //..NB if we had created the record using iDatabase.NewRecordL we would not // have to add the handle attribute manually..BUT then to encode the // the handle attribute would have required us to dig it out of the // record first which seems less robust than what is below. // For 1.1 we might create an EncodeRecordL function for a single record // in the same way we now have an EncodeDbL function for the whole database. // This would then allow us to call iDatabase>NewRecordL, followed by // record.EncodeRecordL. CSdpAttr* handleAttr = CSdpAttr::NewL(0x00, record); record->AddAttribute(handleAttr); handleAttr->BuildUintL(TSdpIntBuf<TSdpServRecordHandle>(Database()->NextFreeHandle())); CSdpAttrValue* handleAttrValue = &(handleAttr->Value()); TUint size = TElementEncoder::EncodedSize(handleAttrValue->Type(), handleAttrValue->DataSize()); HBufC8* bufEncodedHandle = HBufC8::NewL(size); CleanupStack::PushL(bufEncodedHandle); TPtr8 ptrEncodedHandle = bufEncodedHandle->Des(); TElementEncoder ee(ptrEncodedHandle); CAttrEncoderVisitor::EncodeAttributeL(ee, *handleAttrValue); handleAttr->BuildEncodedL(ptrEncodedHandle); CleanupStack::PopAndDestroy(); //bufEncodedHandle //Write record handle back to EPOC client (i.e. user) TSdpServRecordHandlePckgBuf pckg(record->Handle()); pckg() = record->Handle(); aMessage.WriteL(1, pckg); //ss// //Build service class attribute... TInt bufServiceClassLen = aMessage.GetDesLengthL(0); //ss// HBufC8* bufServiceClass = HBufC8::NewLC(bufServiceClassLen); TPtr8 ptrServiceClass = bufServiceClass->Des(); aMessage.ReadL(0, ptrServiceClass); //get message data into buf (via ptr) //ss// TBuf8<2> attrId; attrId.FillZ(2); // Order of following lines important to value in attrId!!! attrId[0] = 0x00; attrId[1] = 0x01; CSdpAttr* attr = CSdpAttr::NewL(0x0001, record); record->AddAttribute(attr); attr->BuildEncodedL(ptrServiceClass); //Add record, now with handle and service class attributes, to database Database()->AddRecord(record); CleanupStack::PopAndDestroy(); //bufServiceClass CleanupStack::Pop(); //record, DatabaseStateChange(); //Return a pointer to the new record return record; }CSdpServRecord* CSdpServer::FindAndCheckServiceRecordForDeletion(const RMessage2& aMessage)/**Checks it's ok to delete a record on the global database using handle sent in message slot 0.Returns the pointer of the record that is to be deleted. **/ { LOG_FUNC TSdpServRecordHandlePckgBuf pckg; TRAPD(err, aMessage.ReadL(0, pckg)); //get message data into buf (via ptr) if(err) { // Client has sent us a dodgy descriptor, punish them! PanicClient(aMessage, ESdpBadDescriptor); return NULL; } //the service record handle should be non-zero __ASSERT_DEBUG(pckg() != 0, Panic(ESdpServerDeleteServiceRecordHandleZero)); CSdpServRecord* record = FindRecordByHandle(pckg()); if(!record) { PanicClient(aMessage, ESdpNonExistantRecordHandle); return NULL; } TRAP(err, CheckAllowedL(aMessage, *record)); if(err) { // Client has been naughty, trying to delete things they shouldn't! PanicClient(aMessage, ESdpBadRequest); return NULL; } //Return a pointer to the record to be deleted return record; }void CSdpServer::UpdateAttributeL(const RMessage2& aMessage)/**Updates/Creates an attribute in a record on the global database using the packaged record handle, the packaged attribute id, and the encoded attribute value sent in message slots 0, 1, and 2. **/ { LOG_FUNC TSdpAttributeIDPckgBuf idPckg; TSdpServRecordHandlePckgBuf handlePckg; aMessage.ReadL(0, handlePckg); //get handle into handle package //the service record handle should be non-zero __ASSERT_DEBUG(handlePckg() != 0, Panic(ESdpServerUpdateAttributeRecordHandleZero)); aMessage.ReadL(1, idPckg); //get id into id package TInt bufLen = aMessage.GetDesLengthL(2); HBufC8* buf = HBufC8::NewLC(bufLen); TPtr8 ptr = buf->Des(); aMessage.ReadL(2, ptr); //get message data into buf (via ptr) CSdpServRecord* record = FindRecordByHandle(handlePckg()); if(!record) { PanicClient(aMessage, ESdpNonExistantRecordHandle); return; } CheckAllowedL(aMessage, *record); UpdateOrCreateAtributeL(*record, idPckg(), ptr); iUuidManager->NotifySdpRecordChange(); CleanupStack::PopAndDestroy(); //buf }void CSdpServer::UpdateOrCreateAtributeL(CSdpServRecord& aRecord, TSdpAttributeID aAttrId, const TDesC8& aBuf) { LOG_FUNC CSdpAttr* attr = FindAttributeByID(aRecord, aAttrId); if(attr) { //Deletes old attr value, and replaces with encoded value found in 'ptr'. attr->BuildEncodedL(aBuf); } else { TPckg<TUint16> idBuf(0); BigEndian::Put16(&idBuf[0], aAttrId); static_cast<CSdpAttr*>(aRecord.BuildUintL(idBuf))->BuildEncodedL(aBuf); } aRecord.RecordStateChange(); }void CSdpServer::DeleteAttribute(const RMessage2& aMessage)/**Deletes an attribute on the global database using the packaged record and the packaged attribute id found in record handle and attribute id sent in message slots 0 and 1. **/ { LOG_FUNC TSdpAttributeIDPckgBuf idPckg; TSdpServRecordHandlePckgBuf handlePckg; TRAPD(err, aMessage.ReadL(0, handlePckg)); //get handle into handle package if(err) { // Client has sent us a dodgy descriptor, punish them! PanicClient(aMessage, ESdpBadDescriptor); return; } //the service record handle should be non-zero __ASSERT_DEBUG(handlePckg() != 0, Panic(ESdpServerDeleteAttributeRecordHandleZero)); TRAP(err, aMessage.ReadL(1, idPckg)); //get id into id package if(err) { // Client has sent us a dodgy descriptor, punish them! PanicClient(aMessage, ESdpBadDescriptor); return; } CSdpServRecord* record = FindRecordByHandle(handlePckg()); if(!record) { PanicClient(aMessage, ESdpNonExistantRecordHandle); return; } TRAP(err, CheckAllowedL(aMessage, *record)); if(err) { // Client has been naughty, trying to delete things they shouldn't! PanicClient(aMessage, ESdpBadRequest); return; } CSdpAttr* attr = FindAttributeByID(*record, idPckg()); if(!attr) { return; } delete attr; record->RecordStateChange(); return; }void CSdpServer::DeleteServiceRecord(CSdpServRecord* aServiceRecord)/**Deletes a record on the global database using a pointer to the record. This is used to delete records added to a session when the session is closed if the records have not been deleted by the client.**/ { LOG_FUNC if (aServiceRecord) { //Destructor of a record removes/destroys all its //attributes and deques it from database list. delete aServiceRecord; DatabaseStateChange(); } }TInt CSdpServer::RunError(TInt aError)/** Handle an error from ServiceL() A bad descriptor error implies a badly programmed client, so panic it; otherwise report the error to the client**/ { LOG_FUNC LOG1(_L("s\tCSdpServer::RunError(): %d"), aError); if (aError==KErrBadDescriptor) { PanicClient(Message(),ESdpBadDescriptor); } else { Message().Complete(aError); } ReStart(); return KErrNone; // handled the error fully }void PanicClient(const RMessage2& aMessage, TSdpClientPanic aPanic)/** Panic the client and complete the message. RMessage2::Panic() also completes the message. This is: (a) important for efficient cleanup within the kernel (b) a problem if the message is completed a second time**/ { LOG_STATIC_FUNC LOG1(_L("s\tPanicClient: Reason = %d"), aPanic); aMessage.Panic(KSdpClientPanic,aPanic); }void PanicServer(TSdpServerPanic aPanic)/**Panic our own thread**/ { LOG_STATIC_FUNC LOG1(_L("s\tPanicServer: Reason = %d"), aPanic); User::Panic(KSdpServerPanic, aPanic); }class CAttrPrintVisitor : public CBase, public MAttributeVisitor {public: CAttrPrintVisitor(){}#ifdef _DEBUG void VisitAttributeL(CSdpAttr& aAttribute)#else void VisitAttributeL(CSdpAttr& /*aAttribute*/)#endif { Indent(); LOG1(_L("Attribute ID: 0x%x"), aAttribute.AttributeID()); } void VisitAttributeValueL(CSdpAttrValue & aValue, TSdpElementType aType) { TBuf16<64> iString; switch (aType) { case ETypeString: iString.Copy(aValue.Des()); LOG1(_L("\"%S\""),&iString); break; case ETypeDES: LOG(_L(" DES")); break; case ETypeUint: LOG1(_L(" UInt:0x%x"), aValue.Uint()); break; case ETypeUUID: LOG(_L(" UUID:0x")); HexDes(aValue.UUID().ShortestForm()); break; case ETypeEncoded: HexDes(aValue.Des()); // simplest break; default: LOG1(_L("type %d"), aType); } } void StartListL(CSdpAttrValueList &/*aList*/) { ++iIndent; Indent(); LOG(_L("{")); } void EndListL() { if(iIndent<=0) { LOG(_L("ERROR! Unmatched EndList!")); __DEBUGGER(); } Indent(); LOG(_L("}")); --iIndent; }private:#ifdef _DEBUG void Indent() {for(TInt i=0; i<iIndent;++i) LOG(_L(" "));} void HexDes(const TDesC8& aDes) { for (TInt i = 0; i < aDes.Length(); ++i) LOG1(_L("%02x"), aDes[i]); };#else void Indent() {} void HexDes(const TDesC8&) {}#endif TInt iIndent; };void FlogDb(CSdpDatabase& aDb) { LOG_STATIC_FUNC LOG(_L("Printing Database...")); for(TServRecordIter recIter(aDb.RecordIter()); recIter; recIter++) {// Iterate thru records in Db LOG1(_L("...Printing Record 0x%x"), (*recIter).Handle()); for(TServAttrIter attrIter((*recIter).AttributeIter()); attrIter; attrIter++) {// Iterate thru attributes in record CAttrPrintVisitor* theVisitor = new CAttrPrintVisitor(); if (theVisitor) { TRAP_IGNORE((*attrIter).AcceptVisitorL(*theVisitor)); delete theVisitor; } } } LOG(_L("End Printing Database...")); }static void RunServerL(/*TSdpServerStart& aStart*/)/**Perform all server initialisation, in particular creation of thescheduler and server and then run the scheduler**/ { LOG_STATIC_FUNC CActiveScheduler *scheduler = new (ELeave) CActiveScheduler; CleanupStack::PushL(scheduler); //1st Push CActiveScheduler::Install(scheduler); // create the server (leave it on the cleanup stack) CSdpServer::NewLC();//2nd Push //#ifdef __SDPSERVER_NO_PROCESSES__ RThread::Rendezvous(KErrNone); // this causes the client's Rendezvous to complete #else // naming the server thread after the server helps to debug panics // ignore the error returned here - it's not critcal User::RenameThread(KSdpServerName); // Initialisation complete, now signal the client RProcess::Rendezvous(KErrNone); // this causes the client's Rendezvous to complete #endif // // Ready to run CActiveScheduler::Start(); // // Cleanup the server and scheduler CleanupStack::PopAndDestroy(2); }TInt E32Main()/**Server process entry-pointRecover the startup parameters and run the server**/ { __UHEAP_MARK;#ifdef TEST_OOM //define TEST_OOM in preprocessor definitions in project settings __UHEAP_SETFAIL(RHeap::ERandom, 100); //Qualified - this results in a false positive error from leavescan (... may employ a macro), because the macro name ends with an 'L'.#endif CTrapCleanup* cleanup=CTrapCleanup::New(); TInt r=KErrNoMemory; if (cleanup) { TRAP(r,RunServerL()); delete cleanup; } // __UHEAP_MARKEND; return r; }//=====================================================================//CSdpServSession//=====================================================================CSdpServer& CSdpServSession::Server() { LOG_FUNC return *static_cast<CSdpServer*>(const_cast<CServer2*>(CSession2::Server())); }// constructor - must pass client to CSession2CSdpServSession::CSdpServSession(const RMessage2& /*aMessage*/) { LOG_FUNC }void CSdpServSession::CreateL(const CServer2& /*aServer*/) { LOG_FUNC User::Leave(KErrNotSupported); //FIXME - PRE SECURE_API - NEEDED TO AVOID COMPILER WARNING }void CSdpServSession::CreateL()/**2nd phase construct for sessions - called by the CServer2 framework**/ { LOG_FUNC //CSession2::CreateL(); // private and does nowt so removed //Add session to server first. If anything leaves, it will be removed by the destructor Server().AddSession(); ConstructL(); }void CSdpServSession::ConstructL() { LOG_FUNC // try to reopen the acceptor Server().Listener().TryRestartL(); }CSdpServSession::~CSdpServSession() { LOG_FUNC Server().DropSession(); //Cleanup any records that have been added during this session but not removed RemoveSessionRecords(); }void CSdpServSession::RemoveSessionRecords() { LOG_FUNC for (TInt index = iSessionRecords.Count()-1; index >= 0; index--) { Server().DeleteServiceRecord(iSessionRecords[index]); } iSessionRecords.Close(); }void CSdpServSession::ServiceL(const RMessage2& aMessage)/** Handle a client request.**/ { LOG_FUNC switch(aMessage.Function()) { case ESdpCreateAgentSubSession: NewSubSessionL(ESdpAgent, aMessage); break; case ESdpCreateDatabaseSubSession: NewSubSessionL(ESdpDatabase, aMessage); break; case ESdpResourceCountMarkStart: ResourceCountMarkStart(); aMessage.Complete(KErrNone); break; case ESdpResourceCountMarkEnd: ResourceCountMarkEnd(aMessage); //ss// aMessage.Complete(KErrNone); break; case ESdpResourceCount: NumResourcesL(aMessage); break; case ESdpCloseSubSession: CloseSubSession(aMessage); break; case ESdpDatabaseCreateServiceRecord: { if (aMessage.Int3() != KSdpDatabaseSubSessionHandle) { PanicClient(aMessage, ESdpBadSubSessionHandle); return; } CSdpServRecord* newRecord = NULL; TRAPD(err, newRecord = Server().CreateServiceRecordL(aMessage)); // Inspect iHandle to see if we panicked the client and already completed if (aMessage.Handle()) { if (newRecord) { // Update our list of records for this session. Ignore error as // we wouldn't want to remove the record if an error did occur, // which is very unlikely as typically only 1 or 2 records are // added per session. err = iSessionRecords.Append(newRecord); if(err != KErrNone) { //if append fails delete the service record created above and return the error code Server().DeleteServiceRecord(newRecord); } } aMessage.Complete(err); } return; } case ESdpDatabaseDeleteServiceRecord: { if (aMessage.Int3() != KSdpDatabaseSubSessionHandle) { PanicClient(aMessage, ESdpBadSubSessionHandle); return; } CSdpServRecord* recordToBeDeleted = Server().FindAndCheckServiceRecordForDeletion(aMessage); // If recordToBeDeleted returned is NULL, PanicClient will have been called so aMessage.Handle() // is zero and we've already completed - see if this is not the case if (aMessage.Handle()) { TInt index = iSessionRecords.Find(recordToBeDeleted); // Find the index of the record before deleting the record // Ignore if the record is not found as it may not have been added // in the first place. if (index >= 0) { iSessionRecords.Remove(index); } Server().DeleteServiceRecord(recordToBeDeleted); aMessage.Complete(KErrNone); } return; } case ESdpDatabaseUpdateAttribute: { if (aMessage.Int3() != KSdpDatabaseSubSessionHandle) { PanicClient(aMessage, ESdpBadSubSessionHandle); return; } TRAPD(err, Server().UpdateAttributeL(aMessage)); // Inspect iHandle to see if we panicked the client and already completed if (aMessage.Handle()) { aMessage.Complete(err); } return; } case ESdpDatabaseDeleteAttribute: { if (aMessage.Int3() != KSdpDatabaseSubSessionHandle) { PanicClient(aMessage, ESdpBadSubSessionHandle); return; } Server().DeleteAttribute(aMessage); // Inspect iHandle to see if we panicked the client and already completed if (aMessage.Handle()) { aMessage.Complete(KErrNone); } return; } case ESdpServerDbgMarkHeap: {#ifdef _DEBUG LOG(_L("\tmark sdp server heap")); __UHEAP_MARK;#endif // _DEBUG aMessage.Complete(KErrNone); break; } case ESdpServerDbgCheckHeap: {#ifdef _DEBUG LOG1(_L("\tcheck sdp server heap (expecting %d cells)"), aMessage.Int0()); __UHEAP_CHECK(aMessage.Int0());#endif // _DEBUG aMessage.Complete(KErrNone); break; } case ESdpServerDbgMarkEnd: {#ifdef _DEBUG LOG1(_L("\tmark end sdp server heap (expecting %d cells)"), aMessage.Int0()); __UHEAP_MARKENDC(aMessage.Int0());#endif // _DEBUG aMessage.Complete(KErrNone); break; } case ESdpServerDbgFailNext: {#ifdef _DEBUG LOG1(_L("\tfail next sdp server alloc (simulating failure after %d allocation(s))"), aMessage.Int0()); if ( aMessage.Int0() == 0 ) { __UHEAP_RESET; } else { __UHEAP_FAILNEXT(aMessage.Int0()); }#endif // _DEBUG aMessage.Complete(KErrNone); break; } default: //not handled here so must be for subsession PanicClient(aMessage, ESdpBadRequest); } }void CSdpServSession::NewSubSessionL(TSubSessionType aType, const RMessage2& aMessage)/** Increment our subsession count and send the SDP subsession handle back to the client. If we succeed, we complete the message. If we fail, we leave and the server sends back the error for us.**/ { LOG_FUNC if (aType!=ESdpDatabase) { User::Leave(KErrNotSupported); } //Write handle to client TPckg<TInt> pckg(KSdpDatabaseSubSessionHandle); TRAPD(err, aMessage.WriteL(3, pckg)); //ss// if(err) { User::Leave(err); } iSubSessionCount++; aMessage.Complete(KErrNone); }void CSdpServSession::DeleteSubsession(TUint aHandle, const RMessage2& aMessage) { LOG_FUNC //panic client if bad handle if ((aHandle != KSdpDatabaseSubSessionHandle) || (iSubSessionCount == 0)) { PanicClient(aMessage, ESdpBadSubSessionRemove); return; } iSubSessionCount--; aMessage.Complete(KErrNone); }void CSdpServSession::CloseSubSession(const RMessage2& aMessage) { LOG_FUNC DeleteSubsession((aMessage).Int3(), aMessage); //calls aMessage.Complete(KErrNone) }void CSdpServSession::NumResourcesL(const RMessage2& aMessage) { LOG_FUNC TPckgBuf<TInt> pckg(CountResources()); aMessage.WriteL(0, pckg); //ss// aMessage.Complete(KErrNone); }TInt CSdpServSession::CountResources() { LOG_FUNC return iSubSessionCount; }//**********************************// CEirPublisherSdpUuidBase//**********************************/**CEirPublisherSdpUuidBase provides base class for Sdp Uuid Eir Publisher.**/CEirPublisherSdpUuidBase::~CEirPublisherSdpUuidBase() { delete iPublisher; }CEirPublisherSdpUuidBase::CEirPublisherSdpUuidBase(CSdpUuidManager& aSdpUuidManager, TEirTag aTag): iParent(aSdpUuidManager), iTag(aTag) { }void CEirPublisherSdpUuidBase::ConstructL() { iPublisher = CEirPublisher::NewL(iTag, *this); }void CEirPublisherSdpUuidBase::UpdateUuids(TInt aLength) { iPublisher->PublishData(aLength); }//**********************************// CEirPublisherSdpUuid16//**********************************/**Provides functionality to publish 16 bit UUIDs to EIR.**/CEirPublisherSdpUuid16* CEirPublisherSdpUuid16::NewL(CSdpUuidManager& aSdpUuidManager) { CEirPublisherSdpUuid16* self = new (ELeave) CEirPublisherSdpUuid16(aSdpUuidManager); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); return self; }CEirPublisherSdpUuid16::CEirPublisherSdpUuid16(CSdpUuidManager& aSdpUuidManager): CEirPublisherSdpUuidBase(aSdpUuidManager, EEirTagSdpUuid16) { }CEirPublisherSdpUuid16::~CEirPublisherSdpUuid16() { delete iExtracted; }// From MEirPublisherNotifiervoid CEirPublisherSdpUuid16::MepnSpaceAvailable(TUint aBytesAvailable) { // Ensure previous memory is freed delete iExtracted, iExtracted = NULL; TBool partial = EFalse; if (aBytesAvailable > 0) { iExtracted = iParent.GetAll16BitUUIDs(aBytesAvailable, partial); if(!iExtracted) { // OOM probably iPublisher->SetData(KNullDesC8, EEirDataPartial); } else if(partial) { iPublisher->SetData(*iExtracted, EEirDataPartial); } else { iPublisher->SetData(*iExtracted, EEirDataComplete); } } }void CEirPublisherSdpUuid16::MepnSetDataError(TInt /*aResult*/) { delete iExtracted; iExtracted = NULL; }//**********************************// CEirPublisherSdpUuid128//**********************************/**Provides functionality to publish 128 bit UUIDs to EIR.**/CEirPublisherSdpUuid128* CEirPublisherSdpUuid128::NewL(CSdpUuidManager& aSdpUuidManager) { CEirPublisherSdpUuid128* self = new (ELeave) CEirPublisherSdpUuid128(aSdpUuidManager); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); return self; }CEirPublisherSdpUuid128::CEirPublisherSdpUuid128(CSdpUuidManager& aSdpUuidManager): CEirPublisherSdpUuidBase(aSdpUuidManager, EEirTagSdpUuid128) { }CEirPublisherSdpUuid128::~CEirPublisherSdpUuid128() { delete iExtracted; }// From MEirPublisherNotifiervoid CEirPublisherSdpUuid128::MepnSpaceAvailable(TUint aBytesAvailable) { // Ensure previous memory is freed delete iExtracted, iExtracted = NULL; TBool partial = EFalse; if (aBytesAvailable > 0) { iExtracted = iParent.GetAll128BitUUIDs(aBytesAvailable, partial); if(!iExtracted) { // OOM probably iPublisher->SetData(KNullDesC8, EEirDataPartial); } else if(partial) { iPublisher->SetData(*iExtracted, EEirDataPartial); } else { iPublisher->SetData(*iExtracted, EEirDataComplete); } } }void CEirPublisherSdpUuid128::MepnSetDataError(TInt /*aResult*/) { delete iExtracted; iExtracted = NULL; }CSdpUuidManager* CSdpUuidManager::NewL(CSdpDatabase& aSdpDatabase) { CSdpUuidManager* self = new (ELeave) CSdpUuidManager(aSdpDatabase); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); return self; }CSdpUuidManager::~CSdpUuidManager() { i16BitUUIDs.Close(); i128BitUUIDs.Close(); delete iEirPublisherSdpUuid16; delete iEirPublisherSdpUuid128; delete iUuidVisitor; }void CSdpUuidManager::NotifySdpRecordChange() { TServRecordIter recIter(iSdpDatabase.RecordIter()); CSdpServRecord* rec = NULL; CSdpAttr* attr = NULL; CSdpAttrValue* serviceClassValue = NULL; CSdpAttrValue* browseGroupValue = NULL; ResetUuids(); while((rec = recIter++) != NULL) {// Iterate thru records in Db TServAttrIter attrIter(rec->AttributeIter()); while((attr = attrIter++) != NULL) { if(attr->AttributeID()==KSdpAttrIdServiceClassIDList) { // Find the value of ServiceClassIDList attribute serviceClassValue = &(attr->Value()); } if(attr->AttributeID()==KSdpAttrIdBrowseGroupList) { // Find the value of BrowseGroupList attribute browseGroupValue = &(attr->Value()); } } if(serviceClassValue && browseGroupValue) { // Both ServiceClassIDList and BrowseGroupList have value iUuidVisitor->GetUuids().Reset(); TRAPD(err, browseGroupValue->AcceptVisitorL(*iUuidVisitor)); if(err == KErrNone) { TUUID publicBrowseGroupUUID = TUUID(static_cast<TUint32>(KPublicBrowseGroupUUID)); if(iUuidVisitor->GetUuids().IsPresent(publicBrowseGroupUUID)) { iUuidVisitor->GetUuids().Reset(); TRAPD(err, serviceClassValue->AcceptVisitorL(*iUuidVisitor)); if(err == KErrNone && iUuidVisitor->GetUuids().Count() > 0) { // In eir, we use the 1st uuid on the list, as it is the most specific one AddUuid(iUuidVisitor->GetUuids()[0]); } } } } // Reset the pointers for these two values serviceClassValue = NULL; browseGroupValue = NULL; } // Only publish UUIDs in public browse group // 16 bit UUIDs TUint8 count = i16BitUUIDs.Count(); // Skip the 1st 16 bit uuid record, it's a SDP service UUID if(count > 0) { // UpdateUuids expects the length in bytes, so multiply number of uuids by 2 iEirPublisherSdpUuid16->UpdateUuids(count<<1); } // 128 bit UUIDs count = i128BitUUIDs.Count(); if(count > 0) { // UpdateUuids expects the length in bytes, so multiply number of uuids by 16 iEirPublisherSdpUuid128->UpdateUuids(count<<4); } }void CSdpUuidManager::AddUuid(TUUID aUuid) { TUint8 count = aUuid.MinimumSize(); if(count == KSizeOf16BitUUID) { // 16 bit UUID i16BitUUIDs.Add(aUuid); } else if(count == KSizeOf128BitUUID) { // 128 bit UUID i128BitUUIDs.Add(aUuid); } else { // 32 bit UUIDs are not supported } }void CSdpUuidManager::ResetUuids() { i16BitUUIDs.Reset(); i128BitUUIDs.Reset(); }HBufC8* CSdpUuidManager::GetAll16BitUUIDs(TInt aBytesAvailable, TBool& aPartial) { // Flatten the UUIDs into a little-endian buffer HBufC8* extracted = HBufC8::New(aBytesAvailable); if(extracted) { TPtr8 ptr = extracted->Des(); TInt i = 0; TInt count = i16BitUUIDs.Count(); // Iterate whilst not exhausted and there's space for a UUID while (i < count && ptr.Length() + KSizeOf16BitUUID <= aBytesAvailable) { // convert to 16 bit Little Endian for EIR TPtrC8 p = i16BitUUIDs[i].ShortestForm(); ptr.Append(p[1]); ptr.Append(p[0]); i++; } __ASSERT_DEBUG(extracted->Length() <= aBytesAvailable, Panic(ESdpServerUuidFlattenBroken)); // Let the caller know if there wasn't enough space for all UUIDs aPartial = EFalse; if (i < count) { aPartial = ETrue; } } return extracted; }HBufC8* CSdpUuidManager::GetAll128BitUUIDs(TInt aBytesAvailable, TBool& aPartial) { // Flatten the UUIDs into a little-endian buffer HBufC8* extracted = HBufC8::New(aBytesAvailable); if(extracted) { TPtr8 ptr = extracted->Des(); TInt i = 0; TInt count = i128BitUUIDs.Count(); // Iterate whilst not exhausted and there's space for a UUID while (i < count && ptr.Length() + KSizeOf128BitUUID <= aBytesAvailable) { // convert to 128 bits Little Endian for EIR for (TUint j = 1; j<= KSdpUUIDMaxLength ;j++) { ptr.Append(i128BitUUIDs[i][KSdpUUIDMaxLength - j]); } i++; } __ASSERT_DEBUG(extracted->Length() <= aBytesAvailable, Panic(ESdpServerUuidFlattenBroken)); // Let the caller know if there wasn't enough space for all UUIDs aPartial = EFalse; if (i < count) { aPartial = ETrue; } } return extracted; }CSdpUuidManager::CSdpUuidManager(CSdpDatabase& aSdpDatabase): iSdpDatabase(aSdpDatabase) { }void CSdpUuidManager::ConstructL() { iUuidVisitor = CAttrUuidVisitor::NewL(); iEirPublisherSdpUuid16 = CEirPublisherSdpUuid16::NewL(*this); iEirPublisherSdpUuid128 = CEirPublisherSdpUuid128::NewL(*this); }