Remove spurious "inline" which hides an exported function from elf2e32 in GCC builds - added to bug 3012
// 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 _DEBUG
PANICCATEGORY("epocsvr");
#endif
GLDEF_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 created
Cancel the shutdown timer if it was running
**/
{
LOG_FUNC
++iSessionCount;
if (iSessionCount > iMaxSessionCount)
{
iMaxSessionCount = iSessionCount;
}
iShutdown.Cancel();
}
void CSdpServer::DropSession()
/**
A session is being destroyed
Start 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 attribute
which, 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 the
scheduler 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-point
Recover 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 CSession2
CSdpServSession::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.
iSessionRecords.Append(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 MEirPublisherNotifier
void 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 MEirPublisherNotifier
void 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);
}