Wednesday, September 19, 2007

Accessing User to User Info in a SIP invite message via sipXtapi

My task: to allow a SIP GUI to access UUI (User to User Information) sent in a SIP invite message

In the GSM-r environment, it is used for sending functional numbers between subscribers and any SIP application in the GSM-r world must support this.

Let's start at the end and end at the beginning...with some forays to the beginning in between

SIP applications built on top of sipXtapi are provided access to the data received via the SIPXTAPI_API. So any data that needs to be accessed by the SIP application will need an API defined here. For the UUI, I defined the following:

SIPXTAPI_API SIPX_RESULT sipxCallGetUsertoUserInfo(const SIPX_CALL hCall,
char* szId,
const size_t iMaxLength) ;

SIPXTAPI_API SIPX_RESULT sipxCallGetUsertoUserInfo(const SIPX_CALL hCall,
char* szId,
const size_t iMaxLength)
{
OsStackTraceLogger stackLogger(FAC_SIPXTAPI, PRI_DEBUG, "sipxCallGetUsertoUserInfo");
OsSysLog::add(FAC_SIPXTAPI, PRI_INFO,
"sipxCallGetUsertoUserInfo hCall=%d",
hCall);

SIPX_RESULT sr = SIPX_RESULT_FAILURE ;
UtlString callId ;
UtlString UUSInfo ;

if (sipxCallGetCommonData(hCall, NULL, &callId, NULL, NULL, NULL, NULL, &UUSInfo))
{
if (iMaxLength)
{
strncpy(szId, UUSInfo.data(), iMaxLength) ;
szId[iMaxLength-1] = 0 ;
}
sr = SIPX_RESULT_SUCCESS ;
}

return sr ;
}

This is an exact copy of another sipxCallGet routine and I did so to
1. Keep a consistant format to the calls
2. To minimize the amount of work I had to do

The API makes a call to sipxCallGetCommonData and so that procedure had to be changed to accomodate this new call. The changes are shown in bold italic (as opposed to non-bold italic for old code)

UtlBoolean sipxCallGetCommonData(SIPX_CALL hCall,
SIPX_INSTANCE_DATA** pInst,
UtlString* pStrCallId,
UtlString* pStrRemoteAddress,
UtlString* pLineId,
UtlString* pGhostCallId,
UtlString* pContactAddress,
UtlString* pUsertoUserInfo,
UtlString* pPriority)
{
OsStackTraceLogger logItem(FAC_SIPXTAPI, PRI_DEBUG, "sipxCallGetCommonData");

UtlBoolean bSuccess = FALSE ;
SIPX_CALL_DATA* pData = sipxCallLookup(hCall, SIPX_LOCK_READ, logItem);
if (pData)
{
if (pInst)
{
*pInst = pData->pInst ;
}

if (pStrCallId)
{
if (pData->sessionCallId)
{
*pStrCallId = *pData->sessionCallId ;
}
else
{
*pStrCallId = *pData->callId ;
}
}

if (pStrRemoteAddress)
{
if (pData->remoteAddress)
{
*pStrRemoteAddress = *pData->remoteAddress;
}
else
{
pStrRemoteAddress->remove(0) ;
}
}

if (pLineId)
{
*pLineId = *pData->lineURI ;
}

if (pGhostCallId)
{
if (pData->ghostCallId)
{
*pGhostCallId = *pData->ghostCallId;
}
}

if (pContactAddress)
{
if (pData->contactAddress)
{
*pContactAddress = *pData->contactAddress;

}
}
if (pUsertoUserInfo)
{
if (pData->UsertoUserInfo)
{
*pUsertoUserInfo = *pData->UsertoUserInfo;
}
}
bSuccess = TRUE ;

sipxCallReleaseLock(pData, SIPX_LOCK_READ, logItem) ;
}

return bSuccess ;
}


sipXCallGetCommonData basically looks up the call data which is stored in SIPX_CALL_DATA. So if there's info that a SIP application needs to access, it needs to be declared there and then stored there so that sipXCallGetCommonData can access it.

Here's the declaration of the new field in SIPX_CALL_DATA

typedef struct SIPX_CALL_DATA
{
UtlString* callId;
UtlString* sessionCallId;
UtlString* ghostCallId;
UtlString* remoteAddress ;
UtlString* lineURI ;
UtlString* contactAddress ;
SIPX_LINE hLine ;
SIPX_INSTANCE_DATA* pInst ;
OsRWMutex* pMutex ;
SIPX_CONF hConf ;
SIPX_SECURITY_ATTRIBUTES security;
SIPX_VIDEO_DISPLAY display;
UtlBoolean bRemoveInsteadOfDrop ; /** Remove the call instead of dropping it
-- this is used as part of consultative
transfer when we are the transfer target
and need to replace a call leg within
the same CpPeerCall. */
SIPX_CALLSTATE_EVENT lastCallstateEvent ;
SIPX_CALLSTATE_CAUSE lastCallstateCause ;

SIPX_MEDIA_EVENT lastLocalMediaAudioEvent ;
SIPX_MEDIA_EVENT lastLocalMediaVideoEvent ;
SIPX_MEDIA_EVENT lastRemoteMediaAudioEvent ;
SIPX_MEDIA_EVENT lastRemoteMediaVideoEvent ;

SIPX_INTERNAL_CALLSTATE state ;
UtlBoolean bInFocus ;
int connectionId; /** Cache the connection id */
SIPX_TRANSPORT hTransport;
bool bHoldAfterConnect; /** Used if we are the transfer target, and the
replaced call is HELD or REMOTE_HELD, then
this flag is set, and indicates that the call
should be placed on hold after the connection
is established. */
bool bCallHoldInvoked; /** Set to true if sipxCallHold has been invoked.
Set to fales if sipxCallUnhold has been invoked. */
bool bTonePlaying;
int nFilesPlaying;
UtlString* UsertoUserInfo;
} SIPX_CALL_DATA ;

Looking up all the uses of SIPX_CALL_DATA forincoming calls, almost all of them are looks ups using sipx_call_lookup. Thankfully (i assume due to good coding practices), there is only one place where SIPX_CALL_DATA is initialized with data for an incoming call, which is in sipxFireCallEvent and so I added some calls there to save the data to SIPX_CALL_DATA .

void sipxFireCallEvent(const void* pSrc,
const char* szCallId,
SipSession* pSession,
const char* szRemoteAddress,
SIPX_CALLSTATE_EVENT event,
SIPX_CALLSTATE_CAUSE cause,
void* pEventData,
const char* szRemoteAssertedIdentity)
{
OsStackTraceLogger stackLogger(FAC_SIPXTAPI, PRI_DEBUG, "sipxFireCallEvent");
OsSysLog::add(FAC_SIPXTAPI, PRI_INFO,
"sipxFireCallEvent Src=%p CallId=%s RemoteAddress=%s Event=%s:%s",
pSrc, szCallId, szRemoteAddress, convertCallstateEventToString(event), convertCallstateCauseToString(cause)) ;

SIPX_CALL hCall = SIPX_CALL_NULL;

SIPX_CALL_DATA* pCallData = NULL;
SIPX_LINE hLine = SIPX_LINE_NULL ;
UtlVoidPtr* ptr = NULL;

SIPX_INSTANCE_DATA* pInst ;
UtlString callId ;
UtlString remoteAddress ;
UtlString lineId ;
UtlString contactAddress ;
SIPX_CALL hAssociatedCall = SIPX_CALL_NULL ;
// Prashant
UtlString UUS;
UtlString Priority;

// If this is an NEW inbound call (first we are hearing of it), then create
// a call handle/data structure for it.
if (event == CALLSTATE_NEWCALL)
{
pCallData = new SIPX_CALL_DATA;
memset((void*) pCallData, 0, sizeof(SIPX_CALL_DATA));
pCallData->state = SIPX_INTERNAL_CALLSTATE_UNKNOWN;

pCallData->callId = new UtlString(szCallId) ;
pCallData->remoteAddress = new UtlString(szRemoteAddress) ;
pCallData->pMutex = new OsRWMutex(OsRWMutex::Q_FIFO) ;

pSession->getUsertoUserInfo(UUS);

Url urlFrom;

pCallData->lineURI = new UtlString(urlFrom.toString()) ;
pCallData->pInst = findSessionByCallManager(pSrc) ;

hCall = gpCallHandleMap->allocHandle(pCallData) ;
pInst = pCallData->pInst ;

if (pEventData)
{
char* szOriginalCallId = (char*) pEventData ;
hAssociatedCall = sipxCallLookupHandle(UtlString(szOriginalCallId), pSrc) ;

// Make sure we remove the call instead of allowing a drop. When acting
// as a transfer target, we are performing surgery on a CpPeerCall. We
// want to remove the call leg -- not drop the entire call.
if ((hAssociatedCall) && (cause == CALLSTATE_CAUSE_TRANSFERRED))
{
// get the callstate of the replaced leg
SIPX_CALL_DATA* pOldCallData = sipxCallLookup(hAssociatedCall, SIPX_LOCK_READ, stackLogger);
bool bCallHoldInvoked = false;
if (pOldCallData)
{
bCallHoldInvoked = pOldCallData->bCallHoldInvoked;
sipxCallReleaseLock(pOldCallData, SIPX_LOCK_READ, stackLogger);
}

if (bCallHoldInvoked)
{
SIPX_CALL_DATA* pData = sipxCallLookup(hCall, SIPX_LOCK_WRITE, stackLogger);
if (pData)
{
pData->bHoldAfterConnect = true;
sipxCallReleaseLock(pData, SIPX_LOCK_WRITE, stackLogger);
}
}
sipxCallSetRemoveInsteadofDrop(hAssociatedCall) ;

SIPX_CONF hConf = sipxCallGetConf(hAssociatedCall) ;
if (hConf)
{
sipxAddCallHandleToConf(hCall, hConf) ;
}
}
else if ((hAssociatedCall) && (cause == CALLSTATE_CAUSE_TRANSFER))
{
// This is the case where we are the transferee -- we want to
// make sure that the new call is part of the conference
SIPX_CONF hConf = sipxCallGetConf(hAssociatedCall) ;
if (hConf)
{
// The original call was part of a transfer -- make sure the
// replacement leg is also part of the conference.
sipxAddCallHandleToConf(hCall, hConf) ;
}
}
}

// Increment call count
pInst->pLock->acquire() ;
pInst->nCalls++ ;
pInst->pLock->release() ;

callId = szCallId ;
remoteAddress = szRemoteAddress ;
lineId = urlFrom.toString() ;

pCallData->UsertoUserInfo= new UtlString(UUS) ;
.
.
.
.



Now to the very beginning for the rest of the explanation. UUI info in an invite message is received as: User-To-User: 0506102011223320;pd=1

First off we'll need to extract the info from the invite message and sipXtapi makes this pretty simple.

We'll need to define the UUI field in SipMessage.h

#define SIP_USER_TO_USER_INFO_FIELD "User-To-User"

and then define a method to extract the UUI in the SIPMessage class.

UtlBoolean SipMessage::getUsertoUserInfo(UtlString& eventField) const

{

const char* value = getHeaderValue(0, SIP_USER_TO_USER_INFO_FIELD);

eventField.remove(0);

if(value)

{

eventField.append(value);

}

return(value != NULL);

}

UtlBoolean SipMessage::getUsertoUserInfo(UtlString& eventField) const

{

const char* value = getHeaderValue(0, SIP_USER_TO_USER_INFO_FIELD);

eventField.remove(0);

if(value)

{

eventField.append(value);

}

return(value != NULL);

}

This is where I got stuck for a while. I know how to extract the data and I know where to store the data for SIP appliation but how do I get the extracted data to the variable where it needs to be stored.

sipXFireCallEvent has access to class SIPSession, but not class SIPmessage. SIPSession in turn is used in methods which have access varaibles from SIpConnection which in turn has access to SIPmessage. So the data would have to be copied from SIPmessage to SIPConnection , then to SIPsession and then frinally copied from SIPsession into SIPX_CALL_DATA. I think at the very least, I should've been able to avoid SIPConnection but that proved impossible. The methods that had access to SIPmessage and SIPsession were not being hit when the data was available yet in SIPMessage. So, no other way that to go via SIPSession

First up, copying the data from SIPMessage to SIPConnection
A new variable in SIPConnection to hold the UUI

UtlString mUsertoUserInfo;

The variable is populated in method processInviteRequestOffering

void SipConnection::processInviteRequestOffering(const SipMessage* request,
int tag,
UtlBoolean doesReplaceCallLegExist,
int replaceCallLegState,
UtlString& replaceCallId,
UtlString& replaceToTag,
UtlString& replaceFromTag)
{
UtlString callId ;

getCallId(&callId) ;
request->getCSeqField(&lastRemoteSequenceNumber, NULL) ;

// Save a copy of the INVITE
inviteMsg = new SipMessage(*request);
inviteFromThisSide = FALSE;
setCallerId();

// Set the to tag if it is not set in the Invite
if(tag >= 0)
{
inviteMsg->setToFieldTag(tag);

// Update the cached from field after saving the tag
inviteMsg->getToUrl(mFromUrl);
}

// Save line Id
UtlString uri;
request->getRequestUri(&uri);
// Convert the URI to name-addr format.
Url parsedUri(uri, TRUE);
// Store into mLocalContact, which is in name-addr format.
parsedUri.toString(mLocalContact);

request->getUsertoUserInfo(mUsertoUserInfo);

Then we need to copy the data from SIPConnection to SIPSession. Again create the variable to hold the data

UtlString mUUSInfo;

And additionally, a new method to populate the new variable

UtlBoolean getUsertoUserInfo(UtlString& UUSinfo);

The methods itself is quite simple:

UtlBoolean SipSession::setUsertoUserInfo(UtlString& UUSinfo)

{

mUUSinfo = UUSInfo;

return(UUSinfo != NULL);

}


The last question is where do you call this methods to populate the info correctly. After much searching, the only place that made sense was in a method called getSession (which seems wierd to me as it seems to be a method that gets data rather than writes data.

UtlBoolean SipConnection::getSession(SipSession& session)
{
UtlString callId;
getCallId(&callId);
SipSession ssn;
UtlString temp;
ssn.setCallId(callId.data());
ssn.setLastFromCseq(mCSeqMgr.getCSeqNumber(CSEQ_ID_INVITE));
ssn.setLastToCseq(lastRemoteSequenceNumber);
ssn.setFromUrl(mFromUrl);
ssn.setToUrl(mToUrl);
// Prashant
ssn.setUsertoUserInfo(mUsertoUserInfo);
.
.
.

And finally, we have to assign the variable to the current instance of the SIPSession

SipSession::operator=(const SipSession& rhs)
{
if (this == &rhs) // handle the assignment to self case
return *this;

UtlString::operator=(rhs); // assign fields for parent class


mLocalUrl = rhs.mLocalUrl;
mRemoteUrl = rhs.mRemoteUrl;
mLocalContact = rhs.mLocalContact;
mRemoteContact = rhs.mRemoteContact;
mInitialMethod = rhs.mInitialMethod;
mInitialLocalCseq = rhs.mInitialLocalCseq;
mInitialRemoteCseq = rhs.mInitialRemoteCseq;
mLastFromCseq = rhs.mLastFromCseq;
mLastToCseq = rhs.mLastToCseq;
mSessionState = rhs.mSessionState;
msLocalRequestUri = rhs.msLocalRequestUri;
msRemoteRequestUri = rhs.msRemoteRequestUri;
msContactUriStr = rhs.msContactUriStr;
mUUSInfo=rhs.mUUSInfo;


At this point, when sipXFireCallEvent is called, mUUSInfo contains the correct inforamtion to be written into SIPX_CALL_DATA and to be used by the SIP_API

3 comments:

Anonymous said...

[url=http://securerucasino.ru][img]http://securerucasino.ru/ia.php?q=1[/img][img]http://securerucasino.ru/ia.php?q=2[/img][img]http://securerucasino.ru/ia.php?q=3[/img][/url]



http://bestplacehjx7606.boxhost.me/k-chemu-snitsya-kazino.html Бесплатный игровой автомат лягушка Игровые автоматы играть безплатно и регестрации Игры всех игровых автоматов [url=http://bestplacermc2353.binhoster.com/kak-obmanut-kazino-v-internete.html]Как обмануть казино в интернете[/url] http://bestplaceopa0194.hostingsiteforfree.com/veb-ruletka-onlayn.html Игровые автоматы играть лягушки бесплатно Играть онлайн бесплатно в игровой автомат лягушки Как взломать live рулетку [url=http://bestplacekva1142.hostingsiteforfree.com/kak-vzlomat-igru-kolybel-magii-vtorzhenie-na-dengi-v-kontakte.html]Как взломать игру колыбель магии вторжение на деньги в контакте[/url] http://bestplacehjx7606.boxhost.me/igrovye-avtomaty-ceny-pinball-ceny.html Код на деньги к игре русская рыбалка 3 Заработок на партнерках интернет-казино Интернет казино онлайн [url=http://bestplacehjx7606.boxhost.me/vulkan-mira.html]Вулкан мира[/url] http://bestplacermc2353.binhoster.com/zhizn-kak-ruletka-aforizmy.html Игровые автоматы online Проценты в казино samp Казино с игрой на центы [url=http://bestplacermc2353.binhoster.com/kazino-ho-voronezh.html]Казино хо воронеж[/url]
[url=http://old.computerbild.ru/news/soft/25042/] самый сильный вулкан[/url]
http://bestplacermc2353.binhoster.com/chat-ruletka-zhenskaya.html [url=http://bestplacehjx7606.boxhost.me/igrat-bessplatno-v-igrovye-avtomaty.html]Играть бессплатно в игровые автоматы[/url] Игровые автоматы играть онлайн бесплатно прямо сейчас

http:/www.liveinternet.ru/users/conemascnor1980a4jvg/post264722287/
http:/www.liveinternet.ru/users/mahenruiholz1985k5ctm/post264625130/
http:/www.liveinternet.ru/users/rhymelictia1980d8c2/post264720970/
http:/www.liveinternet.ru/users/fultuwurla197311z5v/post264885478/
http:/www.liveinternet.ru/users/mahenruiholz1985k5ctm/post264625529/

[url=http:/www.liveinternet.ru/users/stabamrhythen1979fw3l/post264881338/]Игровые автоматы скачать бесплатно адмирал[/url][url=http:/www.liveinternet.ru/users/rhymelictia1980d8c2/post264886062/]Игровой автомат crazy fruits играть бесплатно[/url]
[url=http:/www.liveinternet.ru/users/builynephre1974z4wo9/post264882630/]Казино 777 бесплатно без регистрации[/url]

Anonymous said...

http://twitter.com/9PCDIk4MPe рулетка чате украины Бесплатные игровые автоматы вулкан онлайн Айте станция вулкан чат рулетка взлом mamoleptino321 http://twitter.com/nikita_si_63940 Камеди клаб подпольное казино скачать русская рулетка для сенсорных телефонов Онлайн казино мошенники теория вероятности рулетки
http://twitter.com/vadim_ni_174161 фильм подпольное казино видео эмулятор игрового автомата Налог казино скачать программу для взлома игровых автоматов бесплатно


Игровые автоматы акула играть бесплатно
http://progs.com.ua/?p=334#comment-1353
http://olympiad.rgo.ru/2012/08/v-kyolne-nachalas-mezhdunarodnaya-olimpiada-po-geografii/l2/#comment-1706

Anonymous said...

taking a pill of Viagra might steal speed up the in unison a all the same on the side of weak council clocks to modify to the chic interval zone. http://sildenafilinn.net|buy sildenafil http://sildenafilin.net|sildenafil4sale.net

  • Book reviews