#include "p_apps.h"
#include "p_sip.h"
#include "v_task.h"
#include "my_sip.h"

DCHAR Sip_iCurCall;		// Sip_iCurCall shows which call is currently using UI
DCHAR Sip_iMsgIndex;	// Sip_iMsgIndex shows which call this message belongs to

PXCHAR_D Sip_pCallData;
DCHAR Sip_iBaseIndex;
DCHAR Sip_iReqUdp;
DCHAR Sip_iRespUdp;

CCHAR _cCallBaseIndex[] = {SIP_CALL_INFO_BASE0, SIP_CALL_INFO_BASE1};
CCHAR _cCallReqUdp[] = {SIP_CALL0_REQ_UDP, SIP_CALL1_REQ_UDP};
CCHAR _cCallRespUdp[] = {SIP_CALL0_RESP_UDP, SIP_CALL1_RESP_UDP};

#define CALL_FLAG_NONE		0
#define CALL_FLAG_AUTH		1
#define CALL_FLAG_CALLER	2
#define CALL_FLAG_INVSDP	4
#define CALL_FLAG_100REL	8

#define CALL_STATE_IDLE				0
#define CALL_STATE_INVITING			1
#define CALL_STATE_CONNECTING		2
#define CALL_STATE_CONNECTED		3
#define CALL_STATE_DISCONNECTING	4

static XCHAR _pCallData[SIP_CALL_NUMBER][CALL_INFO_LEN];
static XSHORT _iNotifyCode;
static BOOLEAN _bAcceptCall;
static BOOLEAN _bTransferor = FALSE;	/* the party initiating the transfer */
static BOOLEAN _bTransferee = FALSE;	/* the party being transferred to the Transfer Target. */
static BOOLEAN _bTransferedTo = FALSE;  /* the transfer target */

/**********************************************************************************
***********************************************************************************/
BOOLEAN _IsValidAddr(PXCHAR pPeer, PXCHAR pDstIPAddr)
{
	PXCHAR_D pPeerNumber;
	PXCHAR_D pDstIP;
	PXCHAR_D p;
	PXCHAR_D pDomain;

	pPeerNumber = pPeer;
	pDstIP = pDstIPAddr;

	if (!p_strlen_x(pPeerNumber))	return FALSE;

	if (Sys_bUseService || IsValidIP(Sys_pGWIP))
	{
		if (!Task_bLogoned)		return FALSE;
		if (!p_strcmp_x2x(pPeerNumber, SipGetLocalUser()))		return FALSE;
	}
	else
	{
		p = p_strchr_x(pPeerNumber,':');
		if (p)
		{
			*p = 0;
			p ++;
			Sys_iServicePort = p_atoi(p, 10);
		}
		p = p_strchr_x(pPeerNumber, '@');
		if (p)
		{
			p ++;
			pDomain = p;
		}
		else
		{
			pDomain = pPeerNumber;
		}
		if (!p_String2IP(pDomain, pDstIP))	return FALSE;
		if (!IsValidIP(pDstIP))				return FALSE;		
		if (!p_memcmp_ip(pDstIP, Local_pIPAddress))	return FALSE;

		p = pDomain;
		while (*p)
		{
			if (*p == '*')	*p = '.';
			p ++;
		}
	}
	return TRUE;
}

void _SendRequest(UCHAR iRequest)
{
	DCHAR iTransNo;

	iTransNo = SipAllocTrans(TRUE);
	SipCtSendReq(iRequest, Sip_iReqUdp, iTransNo);
	Sip_pCallData[CALL_INFO_CUR_SEQ] ++;
}

void _SendInvReq(BOOLEAN bReInvite)
{
	SipFillInv(bReInvite);
	Sip_pCallData[CALL_INFO_INV_TRANS] = SipAllocTrans(TRUE);
	SipCtSendReq(SIP_REQ_INVITE, Sip_iReqUdp, Sip_pCallData[CALL_INFO_INV_TRANS]);
	Sip_pCallData[CALL_INFO_INV_SEQ] = Sip_pCallData[CALL_INFO_CUR_SEQ];
	Sip_pCallData[CALL_INFO_CUR_SEQ] ++;
}

void _SendCancelReq()
{
	DCHAR iTransNo;

	SipFillCancel();
	iTransNo = SipAllocTrans(TRUE);
	SipCtSendReq(SIP_REQ_CANCEL, Sip_iReqUdp, iTransNo);
}

void _AcceptCall()
{
	Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_CONNECTED;
	if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_INVSDP)
	{
		SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
		SipFill2xxSdp(FALSE);
	}
	else
	{
		SipFill2xxSdp(TRUE);
	}
	SipStSendResp(Sip_pCallData[CALL_INFO_INV_TRANS], 200);
	if (Sip_iCurCall == Sip_iMsgIndex)
	{
		if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_INVSDP)
		{
			TaskStartRtp();	
		}
		SipConnected();
	}
}

void _GetReqUri(UCHAR iSrcIndex)
{
	DCHAR iDstIndex;

	iDstIndex = (UCHAR)(Sip_iBaseIndex + SIP_CALL_REQUEST_URI);
	if (SipGetUri((PXCHAR)Sdram_pDataCache2, iSrcIndex))
	{
		SdramSaveString((USHORT)(PROTOCOL_SDRAM_POS+iDstIndex), (PXCHAR)Sdram_pDataCache2);
	}
	else
	{
		SdramWriteData((USHORT)(PROTOCOL_SDRAM_POS+iDstIndex), 0);
	}
}

void _OpenReqUdp(UCHAR iInfo)
{
	XCHAR pDstIP[IP_ALEN];
	DSHORT iDstPort;
	DCHAR iDstInfo;

	iDstInfo = iInfo;
	if (SdramReadData((USHORT)(PROTOCOL_SDRAM_POS+iDstInfo)))
	{
		iDstPort = SipGetIPPort(pDstIP, iDstInfo);
		if (IsValidIP(pDstIP))
		{
			UdpOpen(pDstIP, iDstPort, Sys_iRegisterPort, Sip_iReqUdp);
		}
	}
}

void _OpenRespUdp(UCHAR iUdpHead)
{
	DSHORT iDstPort;

	iDstPort = SipGetSentby(SIP_RECV_VIA);
	UdpOpen(Adapter_pReceiveIPAddress, iDstPort, Sys_iRegisterPort, iUdpHead);
}

void _GetRouteAndUri()
{
	PXCHAR_D pCur;
	PXCHAR_D pTemp;
	BOOLEAN bHasLR, bMultiRoute;

	// The received Contact header is remote target
	/*	The UAC uses the remote target and route set to build the Request-URI and 
		Route header field of the request. If the route set is empty, the UAC MUST 
		place the remote target URI into the Request-URI. The UAC MUST NOT add a 
		Route header field to the request.
	*/
	if (!SdramReadData((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_ROUTE_SET)))
	{
		_GetReqUri(SIP_RECV_CONTACT);
		SdramWriteData((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_ROUTE_HEADER), 0);
		if (!Sys_bUseService || !(Sys_iCallType & FAST_START_OR))
		{
			_OpenReqUdp(SIP_RECV_CONTACT);
		}
		return;
	}

	/*	If the route set is not empty, and the first URI in the route set contains the lr parameter (see Section 19.1.1),
		the UAC MUST place the remote target URI into the Request-URI and MUST include a Route header field
		containing the route set values in order, including all parameters.
	*/
	bHasLR = FALSE;
	bMultiRoute = FALSE;
	SdramReadString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_ROUTE_SET));
	pCur = (PXCHAR)Sdram_pDataCache;
	pTemp = p_strchr_x(pCur, ',');
	if (pTemp)	
	{
		bMultiRoute = TRUE;
		pTemp[0] = 0;
	}
	SdramWriteString(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF);	// save the first URI for later use
	while (*pCur)
	{
		if (*pCur == ';')
		{
			if (p_memcmp_nocase_c2x((PXCHAR)(pCur+1), (PCCHAR)"lr", 2))
			{
				bHasLR = TRUE;
				break;
			}
		}
		pCur ++;
	}

//	if (!Sys_bUseService || Sys_iCallType == CALL_TYPE_NORMAL)
	if (!Sys_bUseService || !(Sys_iCallType & FAST_START_OR))
	{
		_OpenReqUdp(SIP_TEMP_BUF);
	}

	if (bHasLR)
	{
		_GetReqUri(SIP_RECV_CONTACT);
		SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_HEADER), (UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET));
		return;
	}

	/*	If the route set is not empty, and its first URI does not contain the lr parameter, the UAC MUST place the first
		URI from the route set into the Request-URI, stripping any parameters that are not allowed in a Request-
		URI. The UAC MUST add a Route header field containing the remainder of the route set values in order,
		including all parameters. The UAC MUST then place the remote target URI into the Route header field as
		the last value.
	*/
	// save the remainder of the route set excluding the first one
	SdramWriteData(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF2, 0);
	if (bMultiRoute)
	{
		SdramReadString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_ROUTE_SET));
		pTemp = p_strchr_x((PXCHAR)Sdram_pDataCache, ',');
		if (pTemp)
		{
			pCur = pTemp + 1;
			pCur += p_count_space(pCur);
			p_strcpy_x2x((PXCHAR)Sdram_pDataCache, pCur);
			SdramWriteString(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF2);
		}
	}

	_GetReqUri(SIP_TEMP_BUF);
	// compose Route header
	if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_CONTACT))
	{
		p_strcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache);
		p_line_backup();
		p_line_start((PXCHAR)Sdram_pDataCache);
		if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF2))
		{
			p_line_update_len();
			p_line_add_comma();
		}
		p_line_add_x((PXCHAR)Sdram_pDataCache2);
		SdramWriteString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_ROUTE_HEADER));
		p_line_restore();
	}
	else
	{
		SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_HEADER), SIP_TEMP_BUF2);
	}
}

void _SignalCallEnd(USHORT iResponse)
{
	DSHORT iResp;

	iResp = iResponse;
	if (iResp == 480 || iResp == 486 || iResp == 503 || iResp == 600 || iResp == 603)
	{
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
	else
	{
		SipCallEnd(SIP_SIGNAL_REORDER);
	}

}

/**********************************************************************************
************  CALL COMMON FUNCTIONS  **********************************************
***********************************************************************************/
void SipTransfertoClear()
{
	DCHAR iAltCall;

	if (Task_bCallTransfer && _bTransferedTo)
	{
		iAltCall = 1 - Sip_iMsgIndex;
		if (!SipCallIsIdle(iAltCall))
		{
			SipSetCallContext(iAltCall);
			SipCallHangup();
		}
		Task_bCallTransfer = FALSE;
		_bTransferedTo = FALSE;
	}
}

BOOLEAN SipCallIsIdle(UCHAR iCallIndex)
{
	return ((_pCallData[iCallIndex][CALL_INFO_STATE] == CALL_STATE_IDLE)? TRUE:FALSE);
}

BOOLEAN SipCallAllIdle()
{
	if (SipCallIsIdle(0) && SipCallIsIdle(1))
		return TRUE;
	else
		return FALSE;
}

BOOLEAN SipMatchCall(UCHAR iCallIndex)
{
	if (SipCmpSdram((UCHAR)(_cCallBaseIndex[iCallIndex] + SIP_CALL_CALL_ID), SIP_RECV_CALL_ID))
	{
		return TRUE;
	}
	return FALSE;
}

UCHAR SipMatchInvTrans(UCHAR iVal)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	if (iTransNo == _pCallData[0][CALL_INFO_INV_TRANS])
		return 0;
	else if (iTransNo == _pCallData[1][CALL_INFO_INV_TRANS])
		return 1;
	else 
		return 2;
}

void SipSetCallContext(UCHAR iIndex)
{
	DCHAR iCallIndex;

	iCallIndex = iIndex;
	Sip_iMsgIndex = iCallIndex;
	Sip_pCallData = _pCallData[iCallIndex];
	Sip_iBaseIndex = _cCallBaseIndex[iCallIndex];
	Sip_iReqUdp = _cCallReqUdp[iCallIndex];
	Sip_iRespUdp = _cCallRespUdp[iCallIndex];
}

void SipInitCall()
{
	DCHAR i;

	for (i = Sip_iBaseIndex; i < (UCHAR)(Sip_iBaseIndex + SIP_CALL_INFO_NUM); i ++)
	{
		SdramWriteData((USHORT)(PROTOCOL_SDRAM_POS+i), 0);
	}
	SdramWriteData((USHORT)(OPTIONS_SDRAM_POS+Sip_iReqUdp), 0);
	SdramWriteData((USHORT)(OPTIONS_SDRAM_POS+Sip_iRespUdp), 0);
	Sip_pCallData[CALL_INFO_CUR_SEQ] = 1;
	Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_IDLE;
	Sip_pCallData[CALL_INFO_FLAG] = CALL_FLAG_NONE;
}


void SipClearTaskFlags()
{
	Task_bCallHold = FALSE;
	Task_bOnHold = FALSE;
	Task_bConnected = FALSE;
	Task_bDisconnecting = FALSE;
	Task_bConnecting = FALSE;
	Task_bCaller = FALSE;
	Sip_iConnMode = SIP_MODE_SENDRECV;
}

void SipCallEnd(UCHAR iType)
{
	DCHAR iAltCall;

	TaskSignalMsg(SIGNAL_STOP_WAITING);
	if (Sip_iCurCall == Sip_iMsgIndex)
	{
		TaskSignalMsg(iType);
	}

	SipInitCall();
	iAltCall = 1 - Sip_iMsgIndex;
	if (SipCallAllIdle())
	{
		SipClearTaskFlags();
		_bTransferor = FALSE;
		_bTransferee = FALSE;
//		if (Sip_iCurCall == Sip_iMsgIndex)
		{
			TaskRestart(iType);
		}
	}
	else
	{
		if (Task_bCallTransfer)
		{
			Sip_iCurCall = iAltCall;
		}
		else
		{
			if (Sip_iCurCall == Sip_iMsgIndex && _pCallData[iAltCall][CALL_INFO_STATE] == CALL_STATE_CONNECTED)
			{
				Task_bCallHold = TRUE;
				Task_bConnected = TRUE;
				Task_bDisconnecting = FALSE;
				Task_bConnecting = FALSE;
				Task_bCaller = FALSE;
				Sip_iCurCall = iAltCall;
				Sip_iConnMode = SIP_MODE_SENDONLY;
				Task_bOnHold = FALSE;
			}
		}
	}
}
/**********************************************************************************
***********************************************************************************/
void SipCallRefResp(USHORT iVal)
{
	DSHORT iResp;

	if (Task_bCallTransfer && _bTransferor)
	{
		iResp = iVal;
		if (iResp < 200)	return;

		if (iResp == 407 || iResp == 401)
		{
			SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
			SipFillRefer();
			_SendRequest(SIP_REQ_REFER);
		}
		else
		{
			if (iResp < 300)
			{
				OutputString(IDS_SIP_REFER_ACCEPTED);
			}
			else
			{
				OutputString(IDS_SIP_REFER_DENIED);
				Task_bCallTransfer = FALSE;
				_bTransferor = FALSE;
				Sip_iCurCall = Sip_iMsgIndex;
				Task_bCallHold = TRUE;
				Task_bOnHold = FALSE;
				Sip_iConnMode = SIP_MODE_SENDONLY;
				SipCallHold();
			}
		}
	}
}

void SipCallByeResp(USHORT iVal)
{
	DSHORT iResp;

	iResp = iVal;
	if (iResp == 407 || iResp == 401)
	{
		SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
		SipFillBye();
		_SendRequest(SIP_REQ_BYE);
	}
	else
	{
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
}

void SipCallCancelResp(USHORT iVal)
{
	DSHORT iResp;

	iResp = iVal;
	if (iResp == 407 || iResp == 401)
	{
		SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
		_SendCancelReq();
	}
	else if (iResp >= 300)
	{
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
}

void SipCallPrackResp(USHORT iVal)
{
	DSHORT iResp;

	iResp = iVal;
	if (iResp < 300)
		return;

	if (iResp == 407 || iResp == 401)
	{
		SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
		SipFillPrack();
		_SendRequest(SIP_REQ_PRACK);
	}
	else
	{
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
}

void SipCallNotifyResp(USHORT iVal)
{
	DSHORT iResp;

	iResp = iVal;
	if (iResp < 200)	return;

	if (iResp == 407 || iResp == 401)
	{
		SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
		SipFillNotify(_iNotifyCode);
		_SendRequest(SIP_REQ_NOTIFY);
		return;
	}

	if (iResp >= 300)
	{
		if (iResp == 481)
		{
			SipCallEnd(SIP_SIGNAL_BUSY);
		}
		else
		{
			SipCallHangup();
		}
	}
}


void SipStartCaller()
{
	SipInitSdp();
	SipNewTag((UCHAR)(Sip_iBaseIndex + SIP_CALL_LOCAL_TAG));
	SipNewCallId((UCHAR)(Sip_iBaseIndex + SIP_CALL_CALL_ID));
	SipNewLocalUri((UCHAR)(Sip_iBaseIndex + SIP_CALL_LOCAL_URI));

	_SendInvReq(FALSE);
	Sip_pCallData[CALL_INFO_FLAG] |= CALL_FLAG_CALLER;
	Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_INVITING;
}

void SipCallForwarded()
{
	XCHAR pDstIP[IP_ALEN];
	DSHORT iDstPort;

	Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_IDLE;
	if (SdramReadData(PROTOCOL_SDRAM_POS+SIP_RECV_CONTACT))
	{
		TaskSignalMsg(SIP_CALL_FORWARD);
		SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_URI), SIP_RECV_CONTACT);
		_GetReqUri(SIP_RECV_CONTACT);
		iDstPort = SipGetIPPort(pDstIP, (UCHAR)(Sip_iBaseIndex + SIP_CALL_REQUEST_URI));
		OutputIPPort(pDstIP, iDstPort, IDS_CHECK_POINT_2);
		if (Sys_bUseService)
		{
			if (IsValidIP(pDstIP))
			{
				UdpOpen(pDstIP, iDstPort, Sys_iRegisterPort, Sip_iReqUdp);
			}
			else
			{
				SipGetRemoteId((UCHAR)(Sip_iBaseIndex + SIP_CALL_REQUEST_URI));
				if (!p_strcmp_x2x(Sys_pRemoteNumber, SipGetLocalUser()))
				{
					SipCallEnd(SIP_SIGNAL_BUSY);
					return;
				}
				UdpOpen(Sys_pServiceIP, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
			}
		}
		else
		{
			if (IsValidIP(Sys_pGWIP))
			{
				UdpOpen(Sys_pGWIP, iDstPort, Sys_iRegisterPort, Sip_iReqUdp);
			}
			else
			{
				if (!IsValidIP(pDstIP) || !p_memcmp_ip(pDstIP, Local_pIPAddress))
				{
					SipCallEnd(SIP_SIGNAL_BUSY);
					return;
				}
				UdpOpen(pDstIP, iDstPort, Sys_iRegisterPort, Sip_iReqUdp);
			}
		}
		SipStartCaller();
	}
	else
	{
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
}

void SipTransferNotify(USHORT iResp)
{
	DCHAR iCurCall, iAltCall;

	if (Task_bCallTransfer && _bTransferee)
	{
		if (iResp >= 200)
		{
			SdramWriteData(PROTOCOL_SDRAM_POS+SIP_CALL_REPLACES, 0);
			SdramWriteData(PROTOCOL_SDRAM_POS+SIP_CALL_REFERRED_BY, 0);	
			Task_bCallTransfer = FALSE;
			_bTransferee = FALSE;
		}
		iCurCall = Sip_iMsgIndex;
		iAltCall = 1 - iCurCall;
		if (!SipCallIsIdle(iAltCall))
		{
			SipSetCallContext(iAltCall);
			SipFillNotify(iResp);
			_SendRequest(SIP_REQ_NOTIFY);
			_iNotifyCode = iResp;
			SipSetCallContext(iCurCall);
		}
	}
}

void SipCallInvResp(USHORT iVal)
{
	DCHAR iAltCall;
	BOOLEAN bPrack;
	DSHORT iResp;

	iResp = iVal;

	OutputVal(iResp, IDS_SIP_RESP_INVITE);
	DebugCString(SipGetRespCode(iResp));
	
	if (iResp < 100)	return;

	if (Sip_pCallData[CALL_INFO_STATE] < CALL_STATE_CONNECTED)
	{
		if (iResp < 200)
		{
			Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_CONNECTING;
			if (iResp > 100)
			{
				bPrack = FALSE;
				if (SipParse100rel(SIP_RECV_REQUIRE))
				{
					if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_100REL)
					{
						if (SipUacCmp100rel())
						{
							bPrack = TRUE;
						}
						else
							return;
					}
					else
					{
						Sip_pCallData[CALL_INFO_FLAG] |= CALL_FLAG_100REL;
						SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_TAG), SIP_RECV_TO_TAG);
						SipGetRouteSet((UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET), SIP_RECV_RECORD_ROUTE);
						_GetRouteAndUri();
						SipUacInit100rel();
						bPrack = TRUE;
					}
				}
				if (bPrack)
				{
					SipFillPrack();
					_SendRequest(SIP_REQ_PRACK);
				}
				if (iResp == 180 || iResp == 183)
				{
					// Net2phone don't want local ringback tone if remote ringback tone is sent
					if (Sip_iBodyLines)
					{
						if (SipSdpParse((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO)))
						{
							SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
							TaskStartRtp();		
						}
					}
					else
					{
						TaskSignalMsg(SIGNAL_RING_BACK);
					}
				}
			}
			SipTransferNotify(iResp);
		}
		else
		{
			SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_TAG), SIP_RECV_TO_TAG);
			SipGetRouteSet((UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET), SIP_RECV_RECORD_ROUTE);
			if (iResp == 302 || iResp == 301)
			{
				SipFillAck(FALSE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				SipInitCall();
				SipCallForwarded();
				return;
			}
			if (iResp == 401 || iResp == 407)
			{
				SipFillAck(FALSE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_AUTH)
				{
					SipCallEnd(SIP_SIGNAL_REORDER);
				}
				else
				{
					SipRemoveBracket((UCHAR)(Sip_iBaseIndex + SIP_CALL_REQUEST_URI), (UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_URI));
					SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
					Sip_pCallData[CALL_INFO_FLAG] |= CALL_FLAG_AUTH;
					Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_INVITING;
					SdramWriteData((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_REMOTE_TAG), 0);
					_SendInvReq(FALSE);
				}
				return;
			}
			_GetRouteAndUri();

			if (iResp < 300)
			{
				Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_CONNECTED;
				Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_AUTH;

				SipFillAck(TRUE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				if (Sip_iBodyLines && !SipSdpParse((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO)))
				{
					SipCallHangup();
					SipTransferNotify(415);
				}
				else
				{
					SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
					TaskStartRtp();		
					SipConnected();
					SipTransferNotify(iResp);
				}
			}
			else
			{
				SipFillAck(FALSE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				_SignalCallEnd(iResp);
				SipTransferNotify(iResp);
			}
		}
	}
	else if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTED)
	{
		if (iResp >= 200)
		{
			if (iResp < 300)
			{
				Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_AUTH;
				SipFillAck(TRUE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				if (Task_bCallHold)
				{
					iAltCall = 1 - Sip_iMsgIndex;
					if (_pCallData[iAltCall][CALL_INFO_STATE] == CALL_STATE_CONNECTING 
						&& !(_pCallData[iAltCall][CALL_INFO_FLAG] & CALL_FLAG_CALLER))
					{
						SipClearTaskFlags();
						Task_bConnecting = TRUE;
						Sip_iCurCall = iAltCall;
						SipConnect();
						TaskSignalMsg(SIGNAL_STOP_WAITING);
					}
					else if (_pCallData[iAltCall][CALL_INFO_STATE] == CALL_STATE_CONNECTED)
					{
						Sip_iCurCall = iAltCall;
						SipCallHold();
					}
				}
				else
				{
					if (Sip_iBodyLines)
					{
						if (SipSdpParse((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO)))
						{
							SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
							TaskStartRtp();		
						}
					}
				}
			}
			else
			{
				SipFillAck(FALSE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				if (iResp == 401 || iResp == 407)
				{
					if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_AUTH)
					{
						if (Task_bCallHold)		Task_bCallHold = FALSE;
					}
					else
					{
						SipCopyAuth((UCHAR)(Sip_iBaseIndex + SIP_CALL_WWW_AUTH), (UCHAR)(Sip_iBaseIndex + SIP_CALL_PROXY_AUTH));
						Sip_pCallData[CALL_INFO_FLAG] |= CALL_FLAG_AUTH;
						_SendInvReq(TRUE);
					}
				}
				else
				{
					if (Task_bCallHold)		Task_bCallHold = FALSE;
				}
			}
		}
	}
	else if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_DISCONNECTING)
	{
		if (iResp < 200)
		{
			Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_CONNECTING;
			SipCallHangup();
		}
		else
		{
			SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_TAG), SIP_RECV_TO_TAG);
			SipGetRouteSet((UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET), SIP_RECV_RECORD_ROUTE);
			_GetRouteAndUri();

			if (iResp < 300)
			{
				Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_CONNECTED;
				SipFillAck(TRUE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				SipCallHangup();
			}
			else
			{
				SipFillAck(FALSE);
				SipCtAckInv(Sip_pCallData[CALL_INFO_INV_TRANS]);
				_SignalCallEnd(iResp);
			}
		}
	}
}

void SipCallHangup()
{
	if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_INVITING)
	{
//		Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_DISCONNECTING;
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
	else if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTED)
	{
		SipFillBye();
		_SendRequest(SIP_REQ_BYE);
		// zte's system will send 407 to BYE
//		if (Sys_iServiceType != SERVICE_TYPE_ZTE)
//		{
//			SipCallEnd();
//		}
	}
	else if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTING)
	{
		if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_CALLER)
		{
			_SendCancelReq();
		}
		else
		{
			SipLoadInv(Sip_iMsgIndex);
			SipCpySdram(SIP_RECV_RECORD_ROUTE, (UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET));
			SipSendFinalResp(Sip_pCallData[CALL_INFO_INV_TRANS], 487);
		}
		Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_DISCONNECTING;
	}
}

/**********************************************************************************
************  UAS FUNCTIONS  *****************************************************
***********************************************************************************/
void SipAckFinalResp()
{
	Sip_iSendLen = p_line_get_len();
	_OpenRespUdp(SIP_RESP_UDP_HEAD);
	SipUdpSend(SIP_RESP_UDP_HEAD);
}

void SipSendFinalResp(UCHAR iTransNo, USHORT iResp)
{
	SipFillFinalResp(iResp, (UCHAR)(Sip_iBaseIndex + SIP_CALL_LOCAL_TAG));
	SipStSendResp(iTransNo, iResp);
}

void SipStartCallee()
{
	if (!Sys_bUseService || !(Sys_iCallType & FAST_START_OR))
	{
		UdpOpen(Adapter_pReceiveIPAddress, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
	}
	else
	{
		UdpOpen(Sys_pServiceIP, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
	}
	SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_CALL_ID), SIP_RECV_CALL_ID);
	SipNewTag((UCHAR)(Sip_iBaseIndex + SIP_CALL_LOCAL_TAG));
	SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_LOCAL_URI), SIP_RECV_TO);
	SipRemoveTag((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_URI), SIP_RECV_FROM);
	SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_TAG), SIP_RECV_FROM_TAG);
	SipCpySdram((UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET), SIP_RECV_RECORD_ROUTE);
	_GetRouteAndUri();

	Sip_pCallData[CALL_INFO_CUR_SEQ] = 1;
	Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_IDLE;
	Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_CALLER;
}

void SipCallForwarding(UCHAR iVal)
{
	XCHAR pDstIP[IP_ALEN];
	DCHAR iTransNo;

	iTransNo = iVal;

	if (_IsValidAddr(Sys_pForwardNumber, pDstIP))
	{
		TaskSignalMsg(SIP_CALL_FORWARD);
		SipSendFinalResp(iTransNo, 302);
	}
	else
	{
		SipSendFinalResp(iTransNo, 410);
	}
}


BOOLEAN _HandleCommonInv(UCHAR iVal)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	Sip_pCallData[CALL_INFO_INV_TRANS] = iTransNo;
	_OpenRespUdp(Sip_iRespUdp);
	SipStSave(iTransNo, SIP_REQ_INVITE, Sip_iRespUdp);

	if (Sip_iBodyLines)
	{
		Sip_pCallData[CALL_INFO_FLAG] |= CALL_FLAG_INVSDP;
		if (!SipSdpParse((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO)))
		{
			SipSendFinalResp(iTransNo, 415);
			return FALSE;
		}
	}
	else
	{
		Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_INVSDP;
	}

	if (!(Sys_iCallType & SIP_PRACK_OR))
	{
		Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_100REL;
	}
	else
	{
		if (SipParse100rel(SIP_RECV_REQUIRE) || SipParse100rel(SIP_RECV_SUPPORTED))
		{
			Sip_pCallData[CALL_INFO_FLAG] |= CALL_FLAG_100REL;
		}
		else
		{
			Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_100REL;
		}
	}
	return TRUE;
}

void _CallRinging(UCHAR iVal)
{
	DCHAR iTransNo;

	iTransNo = iVal;
	if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_100REL)
	{
		SipTransSetFlag100rel(iTransNo);
		SipUasInit100rel(SIP_TEMP_BUF2);
	}
	else
	{
		SdramWriteData(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF2, 0);
	}
	SipFill180((UCHAR)(Sip_iBaseIndex + SIP_CALL_LOCAL_TAG), SIP_TEMP_BUF2);
	SipStSendResp(iTransNo, 180);
	Sip_pCallData[CALL_INFO_STATE] = CALL_STATE_CONNECTING;
	SipSaveInv(Sip_iMsgIndex);
}

void SipCallInvReq()
{
	DCHAR iTransNo, iAltState;

	OutputString(IDS_SIP_INVITE_REQ);
	
	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)
		return;

	SipStartCallee();
	if (!_HandleCommonInv(iTransNo))
		return;

	iAltState = _pCallData[1-Sip_iMsgIndex][CALL_INFO_STATE];
	SipGetRemoteId(SIP_RECV_FROM);
	if (Sys_iForwardType & FORWARD_ALWAYS_OR)
	{
		SipCallForwarding(iTransNo);
	}
	else
	{
		if (iAltState == CALL_STATE_IDLE || iAltState == CALL_STATE_DISCONNECTING)
		{
			SipClearTaskFlags();	// add this to clear task flags to enable it to accept call when disconnecting
			TaskSignalMsg(SIGNAL_INCOMING_CALL);
			if (_bAcceptCall)
			{
				Sip_iCurCall = Sip_iMsgIndex;
				// add this part, some gateway won't generate ringback tone locally
				if (Sys_iServiceType == SIP_SERVICE_TYPE_ITALKBB && (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_INVSDP))
//				if (Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_INVSDP)
				{
					SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
					_CallRinging(iTransNo);
					TaskStartRtp();	
				}
				else
				{
					_CallRinging(iTransNo);
				}
				// end this part
			}
			else
			{
				Sip_iCurCall = 1 - Sip_iMsgIndex;
				if(Sys_iForwardType & FORWARD_BUSY_OR)
				{
					SipCallForwarding(iTransNo);
				}
				else
				{
					SipSendFinalResp(iTransNo, 486);
				}
			}
		}
		else
		{
			if (SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_REPLACES))
			{
				// Attended transfer, transfered-to
				SipClearTaskFlags();
				Sip_iCurCall = Sip_iMsgIndex;
				Task_bCallTransfer = TRUE;
				_bTransferedTo = TRUE;
				_AcceptCall();
			}
			else
			{
				if(Sys_iForwardType & FORWARD_BUSY_OR)
				{
					SipCallForwarding(iTransNo);
				}
				else
				{
//					if (Sys_iForwardType & CALL_WAITING_OR)
					if ((Sys_iForwardType & CALL_WAITING_OR) && (iAltState == CALL_STATE_CONNECTED))
					{
						TaskSignalMsg(SIGNAL_CALL_WAITING);
						_CallRinging(iTransNo);
					}
					else
					{
						SipSendFinalResp(iTransNo, 486);
					}
				}
			}
		}
	}
}

void SipCallReinvReq()
{
	DCHAR iTransNo;

	OutputString(IDS_SIP_REINVITE_REQ);

	if (Sip_pCallData[CALL_INFO_STATE] != CALL_STATE_CONNECTED)
	{
		// Add this for Jack's system. Their retransmission of Invite has different via brach and is
		// treated to be another Invite. Now just ignore this Invite to see if it works.
		return;
	}

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)
		return;

	SipSaveSdp((UCHAR)(_cCallBaseIndex[Sip_iCurCall] + SIP_CALL_SDP_INFO));
	if (!_HandleCommonInv(iTransNo))
		return;

	_AcceptCall();
	if (Sip_iCurCall == Sip_iMsgIndex)
	{
		TaskSignalMsg(SIP_UPDATE_STATE);
	}
	else
	{
		SipLoadSdp((UCHAR)(_cCallBaseIndex[Sip_iCurCall] + SIP_CALL_SDP_INFO));
	}
}

void SipCallAckReq()
{
	BOOLEAN bInform;

	OutputString(IDS_SIP_ACK_REQ);

	bInform = SipStRecvAck(Sip_pCallData[CALL_INFO_INV_TRANS]);
	if (bInform)
	{
		Sip_pCallData[CALL_INFO_FLAG] &= ~CALL_FLAG_INVSDP;
		if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTED)
		{
			if (Sip_iBodyLines)
			{
				if (SipSdpParse((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO)))
				{
					SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
					TaskStartRtp();
					TaskSignalMsg(SIP_UPDATE_STATE);
				}
			}
			//SipConnected();
			SipTransfertoClear();
		}
		else
		{
			SipCallEnd(SIP_SIGNAL_BUSY);
		}
	}
}

void _LineClosed()
{
	if (Sip_iMsgIndex == 0)
	{
		TaskSignalMsg(SIP_LINE0_CLOSED);
	}
	else
	{
		TaskSignalMsg(SIP_LINE1_CLOSED);
	}
}

void SipCallByeReq()
{
	DCHAR iTransNo;

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)	return;

	_OpenRespUdp(Sip_iRespUdp);
	SipStSave(iTransNo, SIP_REQ_BYE, Sip_iRespUdp);

	if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTED)
	{
		SipSendFinalResp(iTransNo, 200);
		SipCallEnd(SIP_SIGNAL_BUSY);
	}
	else if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTING)
	{
		// This shound't happen, but in Huawei's x3000, it happens
		// so we treat this BYE like a CANCEL
		SipSendFinalResp(iTransNo, 200);
		if (Sip_iCurCall == Sip_iMsgIndex)
		{
			TaskSignalMsg(SIP_PEER_HANGUP);
			TaskHangup();
		}
		else
		{
			_LineClosed();
			SipCallHangup();
		}
	}
	else
	{
		SipSendFinalResp(iTransNo, 481);
	}
}

void SipCallCanReq()
{
	DCHAR iTransNo;

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)	return;
	_OpenRespUdp(Sip_iRespUdp);
	SipStSave(iTransNo, SIP_REQ_CANCEL, Sip_iRespUdp);

	if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTING && !(Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_CALLER))
	{
		SipSendFinalResp(iTransNo, 200);
		if (Sip_iCurCall == Sip_iMsgIndex)
		{
			TaskSignalMsg(SIP_PEER_HANGUP);
			TaskHangup();
		}
		else
		{
			_LineClosed();
			SipCallHangup();
		}
	}
	else
	{
		SipSendFinalResp(iTransNo, 400);
	}
}

void SipCallRefReq()
{
	DCHAR iTransNo, iNewCall;
	BOOLEAN bAccept;
	XCHAR pDstIP[IP_ALEN];
	DSHORT iDstPort;

	OutputString(IDS_SIP_REFER_REQ);
	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)		return;
	_OpenRespUdp(Sip_iRespUdp);
	SipStSave(iTransNo, SIP_REQ_REFER, Sip_iRespUdp);

	if (Sip_iMsgIndex == 0)
	{
		if (SipCallIsIdle(1))
		{
			bAccept = TRUE;
			iNewCall = 1;
		}
		else
		{
			bAccept = FALSE;
		}
	}
	else
	{
		if (SipCallIsIdle(0))
		{
			bAccept = TRUE;
			iNewCall = 0;
		}
		else
		{
			bAccept = FALSE;
		}
	}
	if (!bAccept)
	{
		SipSendFinalResp(iTransNo, 488);
		return;
	}
	SipSendFinalResp(iTransNo, 202);

	SipFillNotify(100);
	_SendRequest(SIP_REQ_NOTIFY);
	_iNotifyCode = 100;

	_bTransferee = TRUE;
	// start calling 
	Sip_iCurCall = iNewCall;
	SipSetCallContext(iNewCall);
	SipInitCall();

	_GetReqUri(SIP_RECV_REFER_TO);
	SipAddBracket((UCHAR)(Sip_iBaseIndex + SIP_CALL_REMOTE_URI), (UCHAR)(Sip_iBaseIndex + SIP_CALL_REQUEST_URI));
	SipCpySdram(SIP_CALL_REFERRED_BY, SIP_RECV_REFERRED_BY);
	SipGetReplaces(SIP_CALL_REPLACES, SIP_RECV_REFER_TO);

	if (Sys_bUseService)
	{
		UdpOpen(Sys_pServiceIP, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
	}
	else
	{
		iDstPort = SipGetIPPort(pDstIP, (UCHAR)(Sip_iBaseIndex + SIP_CALL_REQUEST_URI));
		if (IsValidIP(Sys_pGWIP))
		{
			UdpOpen(Sys_pGWIP, iDstPort, Sys_iRegisterPort, Sip_iReqUdp);
		}
		else
		{
			UdpOpen(pDstIP, iDstPort, Sys_iRegisterPort, Sip_iReqUdp);
		}
	}

	SipClearTaskFlags();
	Task_bConnecting = TRUE;
	Task_bCaller = TRUE;
	Task_bCallTransfer = TRUE;
	SipStartCaller();
}

void SipNotifyReq()
{
	DCHAR iTransNo;
	DSHORT iRespCode;
	PXCHAR_D pCur;

	OutputString(IDS_SIP_NOTIFY_REQ);
	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)	return;
	_OpenRespUdp(SIP_RESP_UDP_HEAD);
	SipStSave(iTransNo, SIP_REQ_NOTIFY, SIP_RESP_UDP_HEAD);

	SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_EVENT);
	if (p_memcmp_nocase_c2x((PXCHAR)Sdram_pDataCache, _cEventRefer, EVENT_REFER_LABEL_LEN))
	{
		if (Task_bCallTransfer && _bTransferor)
		{
			if (SipMatchCall(0))		
			{
				SipSetCallContext(0);
			}
			else if (SipMatchCall(1))	
			{
				SipSetCallContext(1);
			}
			else
			{
				SipSendFinalResp(iTransNo, 400);
				return;
			}
			SipSendFinalResp(iTransNo, 200);

			if (Sip_iBodyLines)
			{
				SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_BODY);
				pCur = (PXCHAR)Sdram_pDataCache;
				if (!p_memcmp_c2x(pCur, _cSipVer, SIP_VER_LABEL_LEN-1))
				{
					pCur += SIP_VER_LABEL_LEN;
					iRespCode = p_atoi(pCur, 10);
					if (iRespCode >= 200)
					{	
						Task_bCallTransfer = FALSE;
						_bTransferor = FALSE;
						if (iRespCode < 300)
						{
							SipCallHangup();
						}
						else
						{
							Sip_iCurCall = Sip_iMsgIndex;
							Task_bCallHold = TRUE;
							Task_bOnHold = FALSE;
							Sip_iConnMode = SIP_MODE_SENDONLY;
							SipCallHold();
						}
					}
				}
			}
		}
		else
		{
			SipSendFinalResp(iTransNo, 400);
		}
	}
	else
	{
		if (p_memcmp_nocase_c2x((PXCHAR)Sdram_pDataCache, _cEventMessage, EVENT_MESSAGE_LABEL_LEN))
		{
			TaskHandleData(VOICE_MSG_HINT, (PXCHAR)0, Sip_iBodyLines);
		}
		SipSendFinalResp(iTransNo, 200);
	}
}

void SipMessageReq()
{
	DCHAR iTransNo;

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)	return;
	_OpenRespUdp(SIP_RESP_UDP_HEAD);
	SipStSave(iTransNo, SIP_REQ_MESSAGE, SIP_RESP_UDP_HEAD);

	TaskHandleData(SIP_MESSAGE_HINT, (PXCHAR)0, Sip_iBodyLines);
	SipSendFinalResp(iTransNo, 200);
}

void SipCallInfoReq()
{
	DCHAR i, iTransNo;
	PXCHAR_D pCur;
	XCHAR pKey[2];

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)	return;
	_OpenRespUdp(Sip_iRespUdp);
	SipStSave(iTransNo, SIP_REQ_INFO, Sip_iRespUdp);

	SdramReadString(PROTOCOL_SDRAM_POS+SIP_RECV_CONTENT_TYPE);
	if (p_memcmp_nocase_c2x((PXCHAR)Sdram_pDataCache, Sip_cContentType[SIP_CTYPE_DTMF], p_strlen_c(Sip_cContentType[SIP_CTYPE_DTMF])))
	{
		for (i = 0; i < Sip_iBodyLines; i ++)
		{
			SdramReadString((USHORT)(PROTOCOL_SDRAM_POS+i + SIP_RECV_BODY));
			pCur = (PXCHAR)Sdram_pDataCache;
			if (p_memcmp_nocase_c2x(pCur, (PCCHAR)"Signal=", 7))
			{
				pCur += 7;
				pKey[0] = pCur[0];
				pKey[1] = 0;
				TaskIncomingKeys(1, pKey);
				break;
			}
		}
	}
	else
	{
		TaskHandleData(SIP_INFO_HINT, (PXCHAR)0, Sip_iBodyLines);
	}
	SipSendFinalResp(iTransNo, 200);
}

void SipCallPrackReq()
{
	DCHAR iTransNo;
	BOOLEAN bMatch;

	OutputString(IDS_SIP_PRACK_REQ);

	if (!(Sip_pCallData[CALL_INFO_FLAG] & CALL_FLAG_100REL))
	{
		bMatch = FALSE;
	}
	bMatch = SipUasCmp100rel();
	if (bMatch)
	{
		SipTransClearFlag100rel(Sip_pCallData[CALL_INFO_INV_TRANS]);
	}

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo != SIP_TRANS_ERROR)
	{
		_OpenRespUdp(Sip_iRespUdp);
		SipStSave(iTransNo, SIP_REQ_PRACK, Sip_iRespUdp);
		if (bMatch)
		{
			SipSendFinalResp(iTransNo, 200);
		}
		else
		{
			SipSendFinalResp(iTransNo, 481);
		}
	}
}

void SipCallUpdateReq()
{
	DCHAR iTransNo;

	OutputString(IDS_SIP_UPDATE_REQ);

	iTransNo = SipAllocTrans(FALSE);
	if (iTransNo == SIP_TRANS_ERROR)	return;
	_OpenRespUdp(Sip_iRespUdp);
	SipStSave(iTransNo, SIP_REQ_UPDATE, Sip_iRespUdp);

	if (Sip_iBodyLines)
	{
		if (SipSdpParse((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO)))
		{
			SipFill2xxSdp(FALSE);
			SipStSendResp(iTransNo, 200);
			if (Sip_iMsgIndex == Sip_iCurCall)
			{
				SipLoadSdp((UCHAR)(Sip_iBaseIndex + SIP_CALL_SDP_INFO));
				TaskStartRtp();
			}
		}
		else
		{
			SipSendFinalResp(iTransNo, 415);
		}
	}
	else
	{
		SipSendFinalResp(iTransNo, 200);
	}
}
/**********************************************************************************
****** SIP API functions
***********************************************************************************/
 BOOLEAN SipCall(PXCHAR pKeyBuf)
{
	XCHAR pDstIP[IP_ALEN];
	BOOLEAN bValidPeer;

	bValidPeer = _IsValidAddr(pKeyBuf, pDstIP);
	if (SipCallAllIdle())	
	{
		if (!bValidPeer)	return FALSE;
		SipSetCallContext(0);
	}
	else if (SipCallIsIdle(0))
	{
		if (!bValidPeer)	return TRUE;
		SipSetCallContext(0);
	}
	else if (SipCallIsIdle(1))	
	{
		if (!bValidPeer)	return TRUE;
		SipSetCallContext(1);
	}
	else					
	{
		return TRUE;
	}

	Sip_iCurCall = Sip_iMsgIndex;
	SipInitCall();
	if (Sys_bUseService || IsValidIP(Sys_pGWIP))
	{
		SipNewNameUri((PXCHAR)Sdram_pDataCache, Sys_pRemoteNumber, NULL);
		SdramWriteString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_REMOTE_URI));
		SipNewReqNameUri((PXCHAR)Sdram_pDataCache, Sys_pRemoteNumber);
		SdramWriteString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_REQUEST_URI));
		if (Sys_bUseService)
		{
			UdpOpen(Sys_pServiceIP, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
		}
		else
		{
			UdpOpen(Sys_pGWIP, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
		}
	}
	else
	{
		p_strcpy_c2x((PXCHAR)Sdram_pDataCache, (PCCHAR)"<sip:");
		p_strcat_x2x((PXCHAR)Sdram_pDataCache, pKeyBuf);
		p_strcat_c2x((PXCHAR)Sdram_pDataCache, (PCCHAR)">");
		SdramWriteString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_REMOTE_URI));
		p_strcpy_c2x((PXCHAR)Sdram_pDataCache, (PCCHAR)"sip:");
		p_strcat_x2x((PXCHAR)Sdram_pDataCache, pKeyBuf);
		SdramWriteString((USHORT)(PROTOCOL_SDRAM_POS+Sip_iBaseIndex + SIP_CALL_REQUEST_URI));
		UdpOpen(pDstIP, Sys_iServicePort, Sys_iRegisterPort, Sip_iReqUdp);
	}
	SipClearTaskFlags();
	Task_bConnecting = TRUE;
	Task_bCaller = TRUE;
	SipStartCaller();
	return TRUE;
}

 void SipCallHold()
{
	DCHAR iState, iAltCall;

	SipSetCallContext(Sip_iCurCall);

	iState = Sip_pCallData[CALL_INFO_STATE];
	iAltCall = 1 - Sip_iCurCall;
	if (iState == CALL_STATE_CONNECTED)
	{
		TaskSignalMsg(SIP_STOP_TONE);
		if (!Task_bCallHold)
		{
			Task_bCallHold = TRUE;
			Sip_iConnMode &= ~SIP_MODE_RECVONLY;
			_SendInvReq(TRUE);
		}
		else
		{
			Task_bCallHold = FALSE;
			Sip_iConnMode |= SIP_MODE_RECVONLY;
			_SendInvReq(TRUE);
		}
	}
	else if (iState == CALL_STATE_INVITING || iState == CALL_STATE_CONNECTING)
	{
		if (_pCallData[iAltCall][CALL_INFO_STATE] == CALL_STATE_CONNECTED)
		{
			TaskSignalMsg(SIP_STOP_TONE);
			if (iState == CALL_STATE_INVITING)
			{
				SipInitCall();
			}
			else
			{
				SipCallHangup();
			}
			Sip_iCurCall = iAltCall;
			SipSetCallContext(Sip_iCurCall);
			Task_bCallHold = FALSE;
			Task_bConnected = TRUE;
			Task_bDisconnecting = FALSE;
			Task_bConnecting = FALSE;
			Task_bCaller = FALSE;
			Sip_iConnMode |= SIP_MODE_RECVONLY;
			_SendInvReq(TRUE);
		}
	}
	TaskSignalMsg(SIP_UPDATE_STATE);
}

void _CallTransfer()
{
	SipFillRefer();
	_SendRequest(SIP_REQ_REFER);
	Task_bCallTransfer = TRUE;
	Sip_iConnMode = SIP_MODE_INACTIVE;
}

 void SipCallTransfer(PXCHAR pKeyBuf)
{
	DCHAR iAltCall, iBaseIndex;
	XCHAR pDstIP[IP_ALEN];

	iAltCall = 1 - Sip_iCurCall;
	SipSetCallContext(Sip_iCurCall);
	if (Sip_pCallData[CALL_INFO_STATE] != CALL_STATE_CONNECTED)
		return;

	_bTransferor = TRUE;
	if (_pCallData[iAltCall][CALL_INFO_STATE] == CALL_STATE_CONNECTED)
	{
	//	iBaseIndex = _cCallBaseIndex[Sip_iCurCall];
		iBaseIndex = _cCallBaseIndex[iAltCall];
		p_line_start((PXCHAR)Sdram_pDataCache2);
		p_line_add_p((USHORT)(PROTOCOL_SDRAM_POS+iBaseIndex + SIP_CALL_CALL_ID));
		p_line_add_c((PCCHAR)";to-tag=");
		SdramReadString((USHORT)(PROTOCOL_SDRAM_POS+iBaseIndex + SIP_CALL_REMOTE_TAG));
		p_line_add_x((PXCHAR)(Sdram_pDataCache + TAG_LABEL_LEN));
		p_line_add_c((PCCHAR)";from-tag=");
		SdramReadString((USHORT)(PROTOCOL_SDRAM_POS+iBaseIndex + SIP_CALL_LOCAL_TAG));
		p_line_add_x((PXCHAR)(Sdram_pDataCache + TAG_LABEL_LEN));
		SipEscapeStr((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2);
		SdramWriteString(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF);

		p_line_start((PXCHAR)Sdram_pDataCache2);
		SipRemoveBracket(SIP_TEMP_BUF2, (UCHAR)(iBaseIndex + SIP_CALL_REMOTE_URI));
		p_line_add_p(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF2);
		p_line_add_c((PCCHAR)"?Replaces=");
		p_line_add_p(PROTOCOL_SDRAM_POS+SIP_TEMP_BUF);
		SdramSaveString(PROTOCOL_SDRAM_POS+SIP_CUR_REFER_TO, (PXCHAR)Sdram_pDataCache2);

	//	Sip_iCurCall = iAltCall;
	//	SipSetCallContext(iAltCall);
		_CallTransfer();
	}
	else
	{
		if (!_IsValidAddr(pKeyBuf, pDstIP))
			return;

		if (Sys_bUseService || IsValidIP(Sys_pGWIP))
		{
			SipNewNameUri((PXCHAR)Sdram_pDataCache, pKeyBuf, NULL);
		}
		else
		{
			SipNewIPUri((PXCHAR)Sdram_pDataCache, pDstIP, NULL);
		}
		SdramWriteString(PROTOCOL_SDRAM_POS+SIP_CUR_REFER_TO);
		_CallTransfer();
	}
}

 void SipConnected()
{
	if (!Task_bConnected)	
	{
		TaskConnected();
	}
}

 void SipAnswerCall(BOOLEAN bAccept)
{
	_bAcceptCall = bAccept;
}

 void SipConnect()
{
	SipSetCallContext(Sip_iCurCall);
	if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTING)
	{
		SipLoadInv(Sip_iMsgIndex);
		SipCpySdram(SIP_RECV_RECORD_ROUTE, (UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET));
		_AcceptCall();
	}
}

 void SipCallForward()
{
	SipSetCallContext(Sip_iCurCall);

	if (Sip_pCallData[CALL_INFO_STATE] == CALL_STATE_CONNECTING)
	{
		SipLoadInv(Sip_iMsgIndex);
		SipCpySdram(SIP_RECV_RECORD_ROUTE, (UCHAR)(Sip_iBaseIndex + SIP_CALL_ROUTE_SET));
		SipCallForwarding(Sip_pCallData[CALL_INFO_INV_TRANS]);
	}
}

 void SipHangup()
{
	DCHAR iAltCall;

	Task_bCallTransfer = FALSE;
	_bTransferor = FALSE;
	_bTransferee = FALSE;
	_bTransferedTo = FALSE;

	SipSetCallContext(Sip_iCurCall);
	SipCallHangup();
	// Clear the other connection
	iAltCall = 1 - Sip_iCurCall;
	SipSetCallContext(iAltCall);
	SipCallHangup();
}

 void SipSendDtmfKeys(PXCHAR pKeyBuf, UCHAR iLen)
{
	if (Sys_iDtmfType == SIP_DTMF_SIP_INFO)
	{
		SipSetCallContext(Sip_iCurCall);
		SipFillInfo(pKeyBuf[0]);
		_SendRequest(SIP_REQ_INFO);
	}
	iLen = 0;
}
