messagingappbase/obexmtms/obexmtm/obexutil/source/ObexSdpUtils.cpp
changeset 0 72b543305e3a
child 23 238255e8b033
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingappbase/obexmtms/obexmtm/obexutil/source/ObexSdpUtils.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,695 @@
+// Copyright (c) 2001-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:
+//
+
+//class include
+#include "ObexSdpUtils.h"
+//system includes
+#include <es_sock.h>
+
+
+//
+// Bluetooth Assigned Numbers
+// The following constants come from the Bluetooth Specification Version 1.0B
+//
+
+const TUint32 KObexProtocolUUIDValue = 0x0008;
+const TUint32 KObexObjectPushUUIDValue = 0x1105;
+const TUint32 K_L2CAP_UUID_Value = 0x0100;
+const TUint32 K_RFCOMM_UUID_Value = 0x0003;
+
+const TUint16 KServiceClassIdAttributeId = 0x0001;
+const TUint16 KProtocolDescriptorListAttributeId = 0x0004;
+const TUint16 KBluetoothProfileDescriptorListAttributeId = 0x0009;
+const TUint16 KSupportedFormatsListAttributeId = 0x0303;
+
+
+//
+// The following structures are used to validate the record we receive from a remote
+// device
+//
+
+const TInt KAttributes = 4;
+const TInt KMaxComponents = 8;
+const TInt KTAttributeComponentMembers = 3;
+
+const TUint16 KExpectedAttributes[KAttributes] =
+	{
+	KServiceClassIdAttributeId,
+	KProtocolDescriptorListAttributeId,
+	KBluetoothProfileDescriptorListAttributeId,
+	KSupportedFormatsListAttributeId
+	};
+
+const TUint32 KExpectedComponents[KAttributes][KMaxComponents][KTAttributeComponentMembers] = 
+	{
+		// KServiceClassIdAttributeId 
+		{
+			{CObexSdpUtils::EUint, ETrue, KServiceClassIdAttributeId},
+			{CObexSdpUtils::EAnythingUntilNextExpectedValue, EFalse, 0},
+			{CObexSdpUtils::EUUID, ETrue, KObexObjectPushUUIDValue},
+			{CObexSdpUtils::EAnythingUntilEnd, EFalse, 0}
+		},
+		// KProtocolDescriptorListAttributeId
+		{ 
+			{CObexSdpUtils::EUint, ETrue,  KProtocolDescriptorListAttributeId},
+			{CObexSdpUtils::EAnythingUntilNextExpectedValue, EFalse, 0},
+			{CObexSdpUtils::EUUID, ETrue,  K_L2CAP_UUID_Value},
+			{CObexSdpUtils::EUintPossible, ETrue,  K_RFCOMM_UUID_Value}, //NOT MANDATORY TO RECEIVE THIS UINT
+			{CObexSdpUtils::EUUID, ETrue,  K_RFCOMM_UUID_Value},
+			{CObexSdpUtils::EUint, EFalse, 0}, //REMOTE OBEX PORT NUMBER
+			{CObexSdpUtils::EUUID, ETrue,  KObexProtocolUUIDValue},
+			{CObexSdpUtils::EAnythingUntilEnd, EFalse, 0}
+		},
+		// KBluetoothProfileDescriptorListAttributeId - NOT MANDATORY ATTRIBUTE BUT IF PRESENT INDICATES THAT REMOTE DEVICE CONFORMS 100% TO OBEX OBJECT PUSH PROFILE
+		{
+			{CObexSdpUtils::EUint, ETrue,  KBluetoothProfileDescriptorListAttributeId},
+			{CObexSdpUtils::EAnythingUntilNextExpectedValue, EFalse, 0},
+			{CObexSdpUtils::EUUID, ETrue,  KObexObjectPushUUIDValue},
+			{CObexSdpUtils::EUint, EFalse, 0}, //VERSION OF OBEX OBJECT PUT SUPPORTED
+			{CObexSdpUtils::EAnythingUntilEnd, EFalse, 0}
+		},
+		// KSupportedFormatsListAttributeId
+		{
+			{CObexSdpUtils::EUint, ETrue,  KSupportedFormatsListAttributeId},
+			{CObexSdpUtils::EUintListUntilEnd, EFalse, 0}, // FORMATS SUPPORTED - LIST OF UINTS OF UNSPECIFIED LENGTH
+			{CObexSdpUtils::ENoComponent, EFalse, 0}
+		}
+	};
+
+
+//
+// CObexSdpUtils
+//
+
+
+EXPORT_C CObexSdpUtils* CObexSdpUtils::NewL(MObexSdpUtilsObserver& aObserver)
+/**
+ * Leave safe constructor
+ *
+ * @param aObserver Used to indicate results of SDP query
+ */
+	{
+	CObexSdpUtils* self = new(ELeave) CObexSdpUtils(aObserver);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+EXPORT_C CObexSdpUtils* CObexSdpUtils::NewLC(MObexSdpUtilsObserver& aObserver)
+/**
+ * Leave safe constructor (which adds the newly created object to the cleanup stack)
+ * 
+ * @param aObserver Used to indicate results of SDP query
+ */
+	{
+	CObexSdpUtils* self = new(ELeave) CObexSdpUtils(aObserver);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+void CObexSdpUtils::ConstructL()
+/**
+ * Necessary initial construction - Creates various structures used to check 
+ * results obtained through CSdpAgent
+ */
+	{
+	// Set up structures containing expected components
+	iAttributeArray = new(ELeave) CArrayPtrFlat<CAttribute>(1); //granularity of 1
+
+	TInt attrCount;
+	for (attrCount=0; attrCount<KAttributes; attrCount++)
+		{
+		CObexSdpUtils::CAttribute* attr = new(ELeave) CAttribute;
+		CleanupStack::PushL(attr);
+		attr->iAttributeComponentArray = new(ELeave) CArrayFixFlat<TAttributeComponent>(1); //granularity of 1
+
+		TInt compCount = -1;
+		do
+			{
+			compCount++;
+			TAttributeComponent comp;
+			comp.iType = (TType)KExpectedComponents[attrCount][compCount][0];
+			comp.iSpecificValue = KExpectedComponents[attrCount][compCount][1];
+			comp.iValue = KExpectedComponents[attrCount][compCount][2];
+			attr->iAttributeComponentArray->AppendL(comp);
+			} while (KExpectedComponents[attrCount][compCount][0]!=ENoComponent);
+
+		attr->iAttributeId = KExpectedAttributes[attrCount];
+		iAttributeArray->AppendL(attr);
+		CleanupStack::Pop(attr);
+		}
+	}
+
+
+CObexSdpUtils::CObexSdpUtils(MObexSdpUtilsObserver& aObserver) : iObserver(aObserver), iIsActiveSDPQuery(EFalse)
+
+/**
+ * Simple constructor
+ *
+ * @param aObserver Used to indicate results of SDP query
+ */
+	{
+	}
+
+CObexSdpUtils::~CObexSdpUtils()
+/**
+ * Destructor. This can be called before the SDP Query completes to safely cancel the 
+ * query - N.B. this is the only way to cancel a pending query
+ */
+	{
+	if (iIsActiveSDPQuery)
+		// if we are in the middle of a query
+		{
+			HaltQueryWithError(KErrAbort); 
+			//iObserver.RemoteBtObexQueryResult(KErrAbort,KErrNotFound,EFalse,KErrNotFound,iSupportedFormatsList);
+			//iSdpAgent->Cancel();
+		}
+
+		if(iSdpAgent)
+		// if we have started a SDP agent
+   		{
+			delete iSdpAgent;	
+		
+		}
+
+	iAttributeArray->ResetAndDestroy();
+	delete iAttributeArray;
+	}
+
+
+EXPORT_C void CObexSdpUtils::RemoteBtObexQueryL(TBTDevAddr aBtDevAddr)
+/**
+ * Perform SDP query on a remote bluetooth device. Result is returned through the MObexSdpUtilsObserver 
+ * observer class. The query can be cancelled at any time by destroying this CObexSdpUtils 
+ * object.
+ *
+ * @param aBtDevAddr The address of the bluetooth device
+ */
+	{
+		if(iSdpAgent)
+		{
+			iSdpAgent->Cancel();
+		}
+		else
+		{
+			iSdpAgent = CSdpAgent::NewL(*this,aBtDevAddr);
+		}
+
+	// Set up search for SDP records containing the OBEX Object Push Service 
+	// UUID.
+	// We also specify that the record must contain L2CAP and RFCOMM UUIDS as
+	// these are mandatory according to the BT Spec
+	CSdpSearchPattern* searchPattern = CSdpSearchPattern::NewL();
+	CleanupStack::PushL(searchPattern);
+	searchPattern->AddL(TUUID(KObexObjectPushUUIDValue));
+	searchPattern->AddL(TUUID(K_L2CAP_UUID_Value));
+	searchPattern->AddL(TUUID(K_RFCOMM_UUID_Value));
+	iSdpAgent->SetRecordFilterL(*searchPattern); //makes COPY of searchPattern
+	CleanupStack::PopAndDestroy(searchPattern);
+
+	// Start the search
+	iSdpRecordCount=0;
+	iSdpAgent->NextRecordRequestL(); //Will result in call to NextRecordRequestComplete()
+	iIsActiveSDPQuery = ETrue; //Keep record of the fact that we are mid query
+	}
+
+void CObexSdpUtils::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt aTotalRecordsCount)
+/** 
+ * Got an SDP record from the remote device, so check for errors and then request the 
+ * first attribute.
+ *
+ * @param aError Error code 
+ * @param aHandle Handle to the service recorde
+ * @param aTotalRecordsCount The total number of records
+ */
+	{
+	iSdpRecordCount++;
+
+	if (aError)
+		{
+		// return the error that has prevented us from continuing with our query
+		HaltQueryWithError(aError);
+		return;
+		}
+
+	if(iSdpRecordCount>aTotalRecordsCount)
+		{
+		// return KErrEof because we have gone through all the records and 
+		// the port hasn't been found
+		HaltQueryWithError(KErrEof);
+		return;
+		}
+
+	// Got a record so let's request our first attribute
+	iNotThisSdpRecord = EFalse;
+	iRemoteObexPort = KErrNotFound;
+	iFullComplianceWithObexObjectPush = EFalse;
+	iObexObjectPushProfileVersion = KErrNotFound;
+	iSupportedFormatsList.SetLength(0);
+
+	iAttribute = -1; 
+	RequestNextAttribute(aHandle); //will increment iAttribute to 0
+	}
+
+void CObexSdpUtils::AttributeRequestResult(TSdpServRecordHandle /*aHandle*/, TSdpAttributeID /*aAttrID*/, CSdpAttrValue* /*aAttrValue*/)
+/**
+ * The overload of AttributeRequestL() that we are using in NextRecordRequestComplete()
+ * should NOT result in this function being called, if it does then halt the query with an 
+ * error.
+ *
+ * @param aHandle The handle to the service record
+ * @param aAttrID The SDP Attribute ID
+ * @param aAttrValue The SDP Attribute Value
+ */
+	{
+	// The overload of AttributeRequestL() that we are using in NextRecordRequestComplete
+	// should NOT result in this function being called
+	HaltQueryWithError(KErrUnknown);
+	}
+
+void CObexSdpUtils::AttributeRequestComplete(TSdpServRecordHandle aHandle, TInt aError)
+/**
+ * Called after we have got all the attribute components for current attribute being 
+ * processed. If everything is OK get the next attribute or record.
+ * 
+ * @param aHandle The handle to the service record
+ * @param aError The error code
+ */
+	{
+	if (!aError && !iNotThisSdpRecord && iAttribute>=iAttributeArray->Count()-1 && iExpectedType==EUintListUntilEnd)
+		{
+		// Query is complete - got all the attributes and we have finished in the middle
+		// of compiling the list of supported obex formats (we can not know in advance the 
+		// no. of formats a remote device supports)
+		iObserver.RemoteBtObexQueryResult(KErrNone,iRemoteObexPort,iFullComplianceWithObexObjectPush,iObexObjectPushProfileVersion,iSupportedFormatsList);
+		HaltQuery();
+		}
+	else
+		{
+		// See if we should continue looking at the attributes for this record
+		if (iNotThisSdpRecord //there was a problem with attribute UUIDs and Uints
+		    || (iExpectedType==EAnythingUntilNextExpectedValue) //we should have got whatever we expected next!
+		    || (iExpectedType==EUint) //we should always get the Uint we are looking for
+			|| (iExpectedType==EUUID) //we should always get the UUID we are looking for 
+			|| (aError)) //an error has occured, note that not finding an attribute does NOT result in KErrNotFound
+			{
+			// following is an exception to the rules above...
+			if (iCurrentAttributeId==KBluetoothProfileDescriptorListAttributeId) //it OK to not find what we expect in for attribute because attribute is not mandatory
+				{
+				// therefore everything is OK with the record so far - Request the next attribute
+				RequestNextAttribute(aHandle);
+				}
+			else
+				{
+				// this record didn't satisfy our query so try the next one
+				TRAPD(err,iSdpAgent->NextRecordRequestL()); //results in call to NextRecordRequestComplete()
+				if (err)
+					{
+					HaltQueryWithError(err);
+					}
+				}
+			}
+		else
+			{
+			// Everything is OK with the record so far - Request the next attribute
+			RequestNextAttribute(aHandle);
+			}
+		}
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildUUIDL(const TUUID& aUUID)
+/**
+ * Got a UUID attribute component. Check it is what we expected to receive. Get the next attribute component.
+ *
+ * @param aUUID The Bluetooth UUID of attribute
+ */
+	{
+	if(iNotThisSdpRecord
+	   || iExpectedType==EAnythingUntilEnd) //don't care what we get for current attribute anymore!
+		{
+		// Not interested in this UUID
+		return this;
+		}
+
+	if (iExpectedType==EAnythingUntilNextExpectedValue)
+		{
+		if (iNextExpectedType==EUUID && aUUID==TUUID(iNextExpectedValue))
+			{
+			// Got the "next" value we are looking for so set current expected to "next"
+			// and carry on!
+			NextAttributeComponent();
+			}
+		else
+			{
+			// Not interested in this UUID
+			return this;
+			}
+		}
+
+	iCurrentUUID = aUUID;
+
+	if (iExpectedType!=EUUID)
+		{
+		if (iExpectedType==EUintPossible)
+			{
+			// It was possible to receive a Uint but we got a UUID instead, this is
+			// perfectly valid. Go the the next component and call this method
+			// recursively to ensure next component is processed with this UUID
+			NextAttributeComponent();
+			BuildUUIDL(aUUID);
+			return this; //to prevent us missing an attribute
+			}
+		else
+			{
+			// Shouldn't have received a UUID!
+			iNotThisSdpRecord = ETrue;
+			}
+		}
+	else if (iExpectingSpecific)
+		{
+		if (aUUID != TUUID(iExpectedValue))
+			{
+			// Havn't got what we expected!
+			iNotThisSdpRecord = ETrue;
+			}
+		else if (aUUID==TUUID(KObexObjectPushUUIDValue) && iCurrentAttributeId==KBluetoothProfileDescriptorListAttributeId)
+			{
+			// remote device complies fully with obex object push profile
+			iFullComplianceWithObexObjectPush = ETrue;;
+			}
+		}
+	else
+		{
+		// When dealing with UUIDs we are ALWAYS expecting a specific UUID.
+		// If we end up here then something has gone wrong
+		iNotThisSdpRecord = ETrue;
+		}
+
+	NextAttributeComponent();
+	return this;
+	}
+
+
+MSdpElementBuilder* CObexSdpUtils::BuildUintL(const TDesC8& aUint)
+/**
+ * Got a Uint attribute componet. Check it is what we expected to receive. Get the next attribute component.
+ *
+ * @param aUint Attribute as Uint contained in a descriptor
+ */
+	{
+	if(iNotThisSdpRecord
+	   || iExpectedType==EAnythingUntilEnd) //don't care what we get for current attribute anymore!
+		{
+		// Not interested in this Uint
+		return this;
+		}
+
+	TUint32 uint = UInt32(aUint);
+
+	if (iExpectedType==EAnythingUntilNextExpectedValue)
+		{
+		if (iNextExpectedType==EUint && uint==iNextExpectedValue)
+			{
+			// Got the "next" value we are looking for so set current expected to "next"
+			// and carry on!
+			NextAttributeComponent();
+			}
+		else
+			{
+			// Not interested in this Uint
+			return this;
+			}
+		}
+
+	if (iExpectedType==ENoComponent || iExpectedType==EUUID)
+		{
+		// We should have not found a Uint
+		iNotThisSdpRecord = ETrue;
+		}
+	else if (iExpectingSpecific)
+		{
+		if (uint != iExpectedValue)
+			{
+			// Havn't got what we expected!
+			iNotThisSdpRecord = ETrue;
+			}
+		}
+	else
+		{
+		// Not expecting a specific value so the value we've got is unknown so it's
+		// a piece of info we are trying to discover
+
+		if (iCurrentAttributeId==KProtocolDescriptorListAttributeId && iCurrentUUID==TUUID(K_RFCOMM_UUID_Value))
+			{
+			// Got the port number!
+			iRemoteObexPort = uint;
+			}
+		else if (iCurrentAttributeId==KBluetoothProfileDescriptorListAttributeId && iCurrentUUID==TUUID(KObexObjectPushUUIDValue))
+			{
+			// Got the Obex Object Push Profile Version
+			iObexObjectPushProfileVersion = uint;
+			}
+		else if (iCurrentAttributeId==KSupportedFormatsListAttributeId && iCurrentUUID==TUUID(NULL))
+			{
+			// Got an Obex format supported by the remote device
+			TInt length = iSupportedFormatsList.Length();
+			if (length<KMaxObexSupportedFormats)
+				{
+				iSupportedFormatsList.SetLength(length+1);
+				iSupportedFormatsList[length]=(TUint8)uint;
+				}
+			else
+				{
+				HaltQueryWithError(KErrTooBig);
+				}
+			return this; //don't go to next expected component because a list doesn't
+						 //have a defined length
+			}
+		}
+
+		NextAttributeComponent();
+		return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildUnknownL(TUint8 /*aType*/, TUint8 /*aSizeDesc*/, const TDesC8& /*aData*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildNilL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildIntL(const TDesC8& /*aInt*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildBooleanL(TBool /*aBool*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildStringL(const TDesC8& /*aString*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildDESL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildDEAL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::StartListL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::EndListL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CObexSdpUtils::BuildURLL(const TDesC8& /*aURL*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+	{
+	return this;
+	}
+
+TUint32 CObexSdpUtils::UInt32(const TDesC8& aUint)
+/**
+ * Convert descriptor containing Uint to a numeric Uint.
+ *
+ * The Uints can be 1, 2, 4, 8 or 16 bytes long in theory. It is doubtful 
+ * that the 8 or 16 byte variety would actually be used in our Obex Object 
+ * Push Service case. The attribute Id will always be a 2 byte Uint but it 
+ * is unclear from the spec what size the the port number should be. It is 
+ * implied that it should only be 1 byte but in the EPOC implementation, it 
+ * is returned as whatever size is it registered as (there is no type/size 
+ * checking carried out in the SDP server).
+ *
+ * @param aUint The Uint as a descriptor
+ */
+	{
+	TUint32 uint=0;
+
+	switch(aUint.Length())
+		{
+	case 1:
+		uint = (TUint32)aUint[0];
+		break;
+	case 2:
+		uint = (TUint32)(BigEndian::Get16(aUint.Ptr()));
+		break;
+	case 4:
+	default: 
+		// for our query, if larger Uids are received they may be truncated
+		uint = BigEndian::Get32(aUint.Ptr());
+		break;
+		}
+
+	return uint;
+	}
+
+void CObexSdpUtils::HaltQuery()
+/**
+ * Halt the current SDP query by destroying the CSdpAgent.
+ */
+	{
+		iSdpAgent->Cancel();
+		iIsActiveSDPQuery = EFalse;
+	}
+
+void CObexSdpUtils::HaltQueryWithError(TInt aError)
+/**
+ * Halt the current SDP query by destroying the CSdpAgent. Inform MObexSdpUtilsObserver 
+ * observer that query has completed with an error.
+ * 
+ * @param aError The error code
+ */
+	{
+	iObserver.RemoteBtObexQueryResult(aError,KErrNotFound,EFalse,KErrNotFound,iSupportedFormatsList);
+	HaltQuery();
+	}
+
+void CObexSdpUtils::RequestNextAttribute(TSdpServRecordHandle aHandle)
+/**
+ * Get next attribute if we are expecting another attribute.
+ * 
+ * @param aHandle The handle to the service records
+ */
+	{
+	iAttribute++;
+	if (iAttribute >= iAttributeArray->Count())
+		{
+		// Shouldn't be requesting another attribute as have no expectation
+		// for another attribute
+		HaltQueryWithError(KErrNotFound);
+		}
+	else
+		{
+		iCurrentUUID=TUUID(NULL);
+
+		iAttributeComponent=-1;
+		NextAttributeComponent(); //will increment iAttributeComponent
+
+		iCurrentAttributeId = (*iAttributeArray)[iAttribute]->iAttributeId;
+		TRAPD(err,iSdpAgent->AttributeRequestL(this,aHandle,TSdpAttributeID(iCurrentAttributeId))); //results in multiple calls to BuildUUIDL() & BuildUintL(), ending with 1 call to AttributeRequestComplete()
+		if (err)
+			{
+			HaltQueryWithError(err);
+			}
+		}
+	}
+
+void CObexSdpUtils::NextAttributeComponent()
+/**
+ * Get next attribute component if we are expecting another attribute component. Set 
+ * internal expectations for the next attribute component we receive.
+ */
+	{
+	iAttributeComponent++;
+	if (iAttributeComponent >= (*iAttributeArray)[iAttribute]->iAttributeComponentArray->Count())
+		{
+		// Shouldn't be requesting another attribute component as we have no 
+		// expectation for another attribute component
+		iNotThisSdpRecord = ETrue;
+		return;
+		}
+
+	// Set expectations for the next attribute component we receive
+	iExpectedType = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent].iType;
+	iExpectingSpecific = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent].iSpecificValue;
+	iExpectedValue = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent].iValue;
+
+	if (iExpectedType==EAnythingUntilNextExpectedValue)
+		{
+		iNextExpectedType = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent+1].iType;
+		iNextExpectedValue = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent+1].iValue;
+		}
+	}
+
+
+CObexSdpUtils::CAttribute::CAttribute()
+/**
+ * Simple constructor
+ */
+	{
+	}
+
+CObexSdpUtils::CAttribute::~CAttribute()
+/**
+ * Destructor deletes array of attributes
+ */
+	{
+	delete iAttributeComponentArray;
+	}