/*-------------------------------------------------------------------------
   SIP function copy right information

   Copyright (C) 2006-2009. Tang, Li <tangli@palmmicro.com>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This software is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-------------------------------------------------------------------------*/

#include "version.h"
#include "type.h"

#ifdef CALL_SIP

#include "ar168.h"
#include "core.h"
#include "apps.h"
#include "tcpip.h"
#include "bank5.h"

#define ALPHANUM_NUM	62
const UCHAR _cAlphaNum[ALPHANUM_NUM] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
'U', 'V', 'W', 'X', 'Y', 'Z'
};

const UCHAR _cRequest[SIP_REQ_NUMBER][10] = 
{
	"INVITE", "ACK", "OPTIONS", "BYE", "CANCEL", "REGISTER", "REFER", "NOTIFY", "INFO", "PRACK", "UPDATE", "MESSAGE", "SUBSCRIBE"
};

const UCHAR _cVia[] = "Via: ";
const UCHAR _cFrom[] = "From: ";
const UCHAR _cTo[] = "To: ";
const UCHAR _cCSeq[] = "CSeq: ";
const UCHAR _cCall_ID[] = "Call-ID: ";
const UCHAR _cContact[] = "Contact: ";
const UCHAR _cContent_Length[] = "Content-Length: ";
const UCHAR _cContent_Type[] = "Content-Type: ";
const UCHAR _cExpires[] = "Expires: ";
const UCHAR _cRoute[] = "Route: ";
const UCHAR _cRecord_Route[] = "Record-Route: ";
const UCHAR _cAuthorization[] = "Authorization: ";
const UCHAR _cProxy_Authorization[] = "Proxy-Authorization: ";
const UCHAR _cRAck[] = "RAck: ";
const UCHAR _cSupported[] = "Supported: ";
const UCHAR _cRequire[] = "Require: ";
const UCHAR _cRSeq[] = "RSeq: ";
const UCHAR _cAllow[] = "Allow: ";
const UCHAR _cAccept[] = "Accept: ";
const UCHAR _cReferTo[] = "Refer-To: ";
const UCHAR _cReferredBy[] = "Referred-By: ";
const UCHAR _cReplaces[] = "Replaces: ";
const UCHAR _cSession_Expires[] = "Session-Expires: ";
const UCHAR _cMin_SE[] = "Min-SE: ";
const UCHAR _cEvent[] = "Event: ";

const UCHAR _cTokenTag[] = "tag=";
const UCHAR _cTokenBranch[] = "branch=";
const UCHAR _cTokenMagic[] = "z9hG4bK";
const UCHAR _cTokenSip[] = "sip:";
const UCHAR _cTokenSipVer[] = "SIP/2.0";
const UCHAR _c100rel[] = "100rel";

const UCHAR _cAppSdp[] = "application/sdp";
const UCHAR _cMsgFrag[] = "message/sipfrag";
const UCHAR _cAppDtmf[] = "application/dtmf-relay";
const UCHAR _cTextPlain[] = "text/plain";

const UCHAR _cSend[] = "Send";
const UCHAR _cRtpmap[] = "a=rtpmap:";

#define RESERVED_CHAR_NUM	10
const UCHAR _cReserved[RESERVED_CHAR_NUM] = {';', '/', '?', ':', '@', '&', '=', '+', '$', ','};

const UCHAR _cSipUdp[] = "_sip._udp.";

// functions in sipr.c
void SipRun(UDP_SOCKET pUcb);

void SipSendResp(USHORT sResp);
void sip_send_notify(USHORT sResp);
void sip_send_bye();
void SipUnhold();
BOOLEAN SipXfereeDone(USHORT sResp);

void line_add_options_str(USHORT sOffset)
{
	UCHAR pBuf[MAX_DNS_LEN];

	OptionsGetString(pBuf, sOffset, MAX_DNS_LEN);
	line_add_str(pBuf);
}

#ifdef OEM_IP20
extern UCHAR hotline;

extern UCHAR TransferFlag ;
extern UCHAR TransFlagResp ;
extern UCHAR voice_message ;

extern UDP_SOCKET _pSyslogSocket;
#endif

/**********************************************************************
 ** message compose functions                                        **
 **********************************************************************
 */
BOOLEAN sip_is_reserved(UCHAR iVal)
{
	UCHAR i;

	for (i = 0; i < RESERVED_CHAR_NUM; i ++)
	{
		if (iVal == _cReserved[i])
		{
			return TRUE;
		}
	}
	return FALSE;
}

void sip_escape_str(PCHAR pDstStr, PCHAR pSrcStr)
{
	PCHAR pDst;
	PCHAR pSrc;
	UCHAR iVal;
	UCHAR pEscaped[4];

	pSrc = pSrcStr;
	pDst = pDstStr;
	pEscaped[0] = '%';
	pEscaped[3] = 0;
	while (*pSrc)
	{
		iVal = *pSrc;
		if (sip_is_reserved(iVal))
		{
			pEscaped[1] = char2asc((UCHAR)(iVal >> 4), FALSE);
			pEscaped[2] = char2asc(iVal, FALSE);
			strcpy(pDst, pEscaped);
			pDst += 3;
		}
		else
		{
			*pDst = iVal;
			pDst ++;
		}
		pSrc ++;
	}
	*pDst = 0;
}


PCHAR sip_get_respcode(USHORT sResp)
{
	switch (sResp)
	{
	case 180:
		return (PCHAR)("Ringing");
	case 200:
		return (PCHAR)("OK");
	case 202:
		return (PCHAR)("Accepted");
	case 301:
		return (PCHAR)("Moved Permanently");
	case 302:
		return (PCHAR)("Moved Temporarily");
	case 400:
		return (PCHAR)("Bad Request");
	case 410:
		return (PCHAR)("Gone");
	case 415:
		return (PCHAR)("Unsupported Media Type");
	case 420:
		return (PCHAR)("Bad Extensions");
	case 480:
		return (PCHAR)("Temporarily Unavailable");
	case 481:
		return (PCHAR)("Call Doesn't Exist");
	case 482:
		return (PCHAR)("Loop Detected");
	case 486:
		return (PCHAR)("Busy Here");
	case 487:
		return (PCHAR)("Request Terminated");
	case 488:
		return (PCHAR)("Not Acceptable");
	case 603:
		return (PCHAR)("Declined");
	default:
		return (PCHAR)" ";
	}
}

void sip_new_token(PCHAR pDst, UCHAR iLen)
{
	UCHAR i, index;

	for (i = 0; i < iLen; i ++)
	{
		index = mod_16x8((USHORT)rand(), ALPHANUM_NUM);
		*pDst = _cAlphaNum[index];
		pDst ++;
	}
	*pDst = 0;
}

void sip_new_branch(PCHAR pBranch)
{
	strcpy(pBranch, _cTokenMagic);
	sip_new_token((PCHAR)(pBranch + 7), (UCHAR)(MAX_BRANCH_LEN - 7));
}

void sip_new_callid(PCHAR pCallId)
{
	sip_new_token(pCallId, 16);
	pCallId[16] = '@';
	ip2str(Sys_pIpAddress, (PCHAR)(pCallId + 17));
}

void sip_add_req_line(UCHAR iMethod, PCHAR pUri)
{
	line_start(Adapter_pUdpBuf);
	line_add_str_with_char(_cRequest[iMethod], ' ');
	line_add_str_with_char(pUri, ' ');
	line_add_str(_cTokenSipVer);
	line_add_crlf();
}

void sip_add_status_line(USHORT sResp)
{
	line_add_str_with_char(_cTokenSipVer, ' ');
	line_add_short(sResp);
	line_add_char(' ');
	line_add_str(sip_get_respcode(sResp));
	line_add_crlf();
}

void sip_add_token(PCHAR pToken, PCHAR pValue)
{
	if (pValue)
	{
		line_add_char(';');
		line_add_str(pToken);
		line_add_str(pValue);
	}
}

void sip_add_via(PCHAR pBranch)
{
	line_add_str(_cVia);
	line_add_str(_cTokenSipVer);
	line_add_str("/UDP ");
	line_add_ip(Sip_pNatIP);
	line_add_char(':');
	line_add_short(Sip_sNatPort);
	sip_add_token(_cTokenBranch, pBranch);
	line_add_str(";rport");
	line_add_crlf();
}

void sip_add_local_uri(BOOLEAN bIP, BOOLEAN bPort)
{
	UCHAR pTemp[MAX_DNS_LEN];

	OptionsGetString(pTemp, OPT_USER_NAME, MAX_USER_NAME_LEN);
	if (strlen(pTemp))
	{
		line_add_char('"');
		line_add_str(pTemp);
		line_add_str("\" ");
	}
	line_add_char('<');
	line_add_str(_cTokenSip);
	OptionsGetString(pTemp, OPT_USER_NUMBER, MAX_USER_NUMBER_LEN);
	if (strlen(pTemp))
	{
		line_add_str_with_char(pTemp, '@');
	}
	if (bIP)
	{
		line_add_ip(Sip_pNatIP);
	}
	else
	{
		line_add_options_str(OPT_SIP_DOMAIN);
	}
	if (bPort)
	{
		line_add_char(':');
		line_add_short(Sip_sNatPort);
	}
	line_add_char('>');
}

void sip_add_to(PCHAR pUri, PCHAR pTag)
{
	line_add_str_with_char(_cTo, '<');
	line_add_str_with_char(pUri, '>');
	sip_add_token(_cTokenTag, pTag);
	line_add_crlf();
}

void sip_add_call_id(PCHAR pCallId)
{
	line_add_str(_cCall_ID);
	line_add_str(pCallId);
	line_add_crlf();
}

void sip_add_contact()
{
	UCHAR pTemp[MAX_DNS_LEN];

	line_add_str_with_char(_cContact, '<');
	line_add_str(_cTokenSip);
	OptionsGetString(pTemp, OPT_USER_NUMBER, MAX_USER_NUMBER_LEN);
	if (strlen(pTemp))
	{
		line_add_str_with_char(pTemp, '@');
	}
	line_add_ip(Sip_pNatIP);
	line_add_char(':');
	line_add_short(Sip_sNatPort);
	line_add_char('>');
	line_add_crlf();
}

void sip_add_fwdcontact()
{
	line_add_str_with_char(_cContact, '<');
	line_add_str(_cTokenSip);
	line_add_options_str(OPT_FWD_NUMBER);
	if (Sys_bRegister)
	{
		line_add_char('@');
		line_add_options_str(OPT_SIP_DOMAIN);
	}
	line_add_char('>');
	line_add_crlf();
}

void sip_add_referredby()
{
	line_add_str(_cReferredBy);
	sip_add_local_uri(!Sys_bRegister, FALSE);
	line_add_crlf();
}

void sip_add_cseq(ULONG lSeq, UCHAR iMethod)
{
	line_add_str(_cCSeq);
	line_add_long(lSeq);
	line_add_char(' ');
	line_add_str(_cRequest[iMethod]);
	line_add_crlf();
}

void sip_add_content_length(USHORT sLength)
{
	line_add_str(_cContent_Length);
	line_add_short(sLength);
	line_add_crlf();
}

void sip_add_content_type(PCHAR pType)
{
	line_add_str(_cContent_Type);
	line_add_str(pType);
	line_add_crlf();
}

void sip_add_supported()
{
	line_add_str(_cSupported);
	if (OptionsGetByte(OPT_PRACK))
	{
		line_add_str_with_char(_c100rel, ',');
	}
	line_add_str("replaces,timer");
	line_add_crlf();
}

void sip_add_allow()
{
	UCHAR i;

	line_add_str(_cAllow);
	for (i = 0; i < SIP_REQ_NUMBER-1; i ++)
	{
		line_add_str_with_char(_cRequest[i], ',');
	}
	line_add_str(_cRequest[i]);
	line_add_crlf();
}

void sip_add_accept()
{
	line_add_str(_cAccept);
	line_add_str_with_char(_cAppSdp, ',');
	line_add_str_with_char(_cMsgFrag, ',');
	line_add_str_with_char(_cAppDtmf, ',');
	line_add_str(_cTextPlain);
	line_add_crlf();
}

void sip_add_require(PCHAR pTag)
{
	line_add_str(_cRequire);
	line_add_str(pTag);
	line_add_crlf();
}

void sip_add_rseq(ULONG lSeq)
{
	line_add_str(_cRSeq);
	line_add_long(lSeq);
	line_add_crlf();
}

void sip_add_max_forwards()
{
	line_add_str("Max-Forwards: 70\r\n");
}

void sip_add_proxy_require()
{
	UCHAR pBuf[MAX_DNS_LEN];

	OptionsGetString(pBuf, OPT_PROXY_REQUIRE, MAX_DNS_LEN);
	if (strlen(pBuf))
	{
		line_add_str("Proxy-Require: ");
		line_add_str(pBuf);
		line_add_crlf();
	}
}

void sip_add_user_agent()
{
#ifndef OEM_IP20
	UCHAR pBuf[32];
#endif

	line_add_str("User-Agent: ");
#ifdef OEM_IP20
	line_add_str("IP20_AAT");
#else
	line_add_options_str(OPT_FLAG_PRODUCT);
	line_add_char(' ');
	TaskLoadString(IDS_VERSION, pBuf);
	line_add_str((PCHAR)(pBuf+5));
#endif
	line_add_crlf();
}

void sip_add_expires(USHORT sVal)
{
	line_add_str(_cExpires);
	line_add_short(sVal);
	line_add_crlf();
}

void sip_add_session_expires(SIP_LCB_HANDLE pLcb)
{
	if (pLcb->sSessionExpires)
	{
		line_add_str(_cSession_Expires);
		line_add_short(pLcb->sSessionExpires);
		if (pLcb->iRefresher)
		{
			line_add_str(";refresher=");
			if (pLcb->iRefresher == SIP_REFRESHER_UAC)
			{
				line_add_str("uac");
			}
			else
			{
				line_add_str("uas");
			}
		}
		line_add_crlf();
	}
	if (pLcb->sMinSE)
	{
		line_add_str(_cMin_SE);
		line_add_short(pLcb->sMinSE);
		line_add_crlf();
	}
}

void sip_add_header(PCHAR pName, PCHAR pHeapBuf)
{
	if (pHeapBuf)
	{
		line_add_str(pName);
		line_add_str(pHeapBuf);
		line_add_crlf();
	}
}

void sip_new_random(PCHAR pBuf, UCHAR iLen)
{
	UCHAR i;
	PCHAR pDst;

	pDst = pBuf;
	for (i = 0; i < iLen; i ++)
	{
		*pDst = mod_16x8((USHORT)rand(), 10) + '0';
		pDst ++;
	}
	*pDst = 0;
}

void sip_debug(PCHAR pStrInfo, USHORT iMessage)
{
	UCHAR pDebug[32];

	strcpy(pDebug, pStrInfo);
	strcat(pDebug, ": ");
	if (iMessage < SIP_REQ_NUMBER)
	{
		strcat(pDebug, _cRequest[iMessage]);
	}
	else
	{
		itoa(iMessage, (PCHAR)(pDebug + strlen(pDebug)), 10);
	}
	UdpDebugString(pDebug);
}

/**********************************************************************
 ** SDP functions                                                 **
 **********************************************************************
 */
const UCHAR _cAudioMode[][9] = {"inactive", "recvonly", "sendonly", "sendrecv"};
const UCHAR Rtp_cCoderName[VOICE_CODER_NUM][8] = {"PCMU", "PCMA", "G726-32", "GSM", "iLBC", "speex", "G729"};
const UCHAR _cAudioTxMode[4] = {AUDIO_MODE_INACTIVE, AUDIO_MODE_SENDONLY, AUDIO_MODE_RECVONLY, AUDIO_MODE_SENDRECV};

void sip_add_fmtp(PCHAR pInfo, UCHAR iPayload)
{
	line_add_str("a=fmtp:");
	line_add_short(iPayload);
	line_add_char(' ');
	line_add_str(pInfo);
	line_add_crlf();
}

void sip_add_rtpmap(PCHAR pInfo, UCHAR iPayload)
{
	line_add_str(_cRtpmap);
	line_add_short(iPayload);
	line_add_char(' ');
	line_add_str(pInfo);
	line_add_str("/8000\r\n");
}

// This should be called before adding data to UdpSendBuf
USHORT sip_add_sdp(SIP_LCB_HANDLE pLcb)
{
	UCHAR i, iDtmfPayload, iVoiceCoder, iMode, iTemp;
	BOOLEAN bVad;
	USHORT sRtpPort;
	UCHAR pPayload[VOICE_CODER_NUM];
	UCHAR pCoder[VOICE_CODER_NUM];
	UCHAR pBuf[9];
	UCHAR iCoderNum;
	USHORT sLen;

	if (pLcb)
	{
		sRtpPort = pLcb->sRtpPort;
		bVad = pLcb->bVad;
		if (pLcb->iSdpType == SDP_TYPE_OFFER)
		{
			iVoiceCoder = VOICE_CODER_NONE;
			iMode = pLcb->iLocalMode;
		}
		else if (pLcb->iSdpType == SDP_TYPE_REINVITE)
		{
			iVoiceCoder = pLcb->iVoiceCoder;
			iMode = pLcb->iLocalMode;
		}
		else
		{
			iVoiceCoder = pLcb->iVoiceCoder;
			iMode = _cAudioTxMode[pLcb->iRemoteMode];
		}
	}
	else
	{
		iVoiceCoder = VOICE_CODER_NONE;
		iMode = AUDIO_MODE_SENDRECV;
		sRtpPort = Sip_sRtpPort;
		bVad = OptionsGetByte(OPT_VAD);
	}
	if (OptionsGetByte(OPT_DTMF_TYPE) == DTMF_TYPE_RFC2833)
	{
		iDtmfPayload = OptionsGetByte(OPT_DTMF_PAYLOAD);
	}
	else
	{
		iDtmfPayload = 0;
	}

	sLen = line_get_len();
	line_add_str("v=0\r\no=- ");

	if (pLcb)
	{
		line_add_str(pLcb->pSdpSessionId);
		line_add_char(' ');
		line_add_long(pLcb->lSdpVersion);
		pLcb->lSdpVersion ++;
	}
	else
	{
		sip_new_random(pBuf, 8);
		line_add_str(pBuf);
		line_add_char(' ');
		sip_new_random(pBuf, 8);
		line_add_str(pBuf);
	}
	line_add_str(" IN IP4 ");
	line_add_ip(Sip_pNatIP);

	line_add_str("\r\ns=SIP CALL\r\nc=IN IP4 ");
#ifdef OEM_BT2008
	// Siemens's switch doesn't support the old "0.0.0.0" hold scheme
	line_add_ip(Sip_pNatIP);
#else
	if (iMode & AUDIO_MODE_RECVONLY)
	{
		line_add_ip(Sip_pNatIP);
	}
	else
	{
		line_add_str("0.0.0.0");
	}
#endif

	line_add_str("\r\nt=0 0\r\nm=audio ");
	line_add_short(sRtpPort);
	line_add_str(" RTP/AVP");

	if (iVoiceCoder != VOICE_CODER_NONE)
	{
		pPayload[0] = RtpGetPayload(iVoiceCoder, pLcb);
		pCoder[0] = iVoiceCoder;
		iCoderNum = 1;
	}
	else
	{
		iCoderNum = 0;
		// this is a SDP offer
		for (i = 0; i < VOICE_CODER_NUM; i ++)
		{
			iTemp = OptionsGetByte(OPT_VOICE_CODER0 + i);
			if (iTemp != VOICE_CODER_NONE && iTemp <= VOICE_CODER_NUM)
			{
				pPayload[iCoderNum] = RtpGetPayload(iTemp, pLcb);
				pCoder[iCoderNum] = iTemp;
				iCoderNum ++;
			}
		}
	}
	for (i = 0; i < iCoderNum; i ++)
	{
		line_add_char(' ');
		line_add_short(pPayload[i]);
	}
	if (iDtmfPayload)
	{
		line_add_char(' ');
		line_add_short(iDtmfPayload);
	}
	line_add_crlf();

	for (i = 0; i < iCoderNum; i ++)
	{
		iVoiceCoder = pCoder[i];
		iTemp = pPayload[i];
		sip_add_rtpmap(Rtp_cCoderName[iVoiceCoder-1], iTemp);
		if (iVoiceCoder == VOICE_CODER_G729)
		{
			if (!bVad)
			{
				sip_add_fmtp("annexb=no", iTemp);
			}
		}
		else if (iVoiceCoder == VOICE_CODER_ILBC)
		{
			if (Sys_iIlbcFrame)
			{
				sip_add_fmtp("mode=30", iTemp);
			}
			else
			{
				sip_add_fmtp("mode=20", iTemp);
			}
		}
	}
	if (iDtmfPayload)
	{
		sip_add_rtpmap("telephone-event", iDtmfPayload);
		sip_add_fmtp("0-15", iDtmfPayload);
	}
	
	line_add_str("a=");
	line_add_str(_cAudioMode[iMode]);
	line_add_crlf();

	sLen = line_get_len() - sLen;
	return sLen;
}

/**********************************************************************
 ** transaction layer send functions                                 **
 **********************************************************************
 */
// transaction timer
#define TR_TIMER_A		2	// retransmit interval, for UDP only
#define TR_TIMER_B		11	// transaction timeout timer
#ifdef OEM_IP20
#define TR_TIMER_D		0
#else
#define TR_TIMER_D		4	// Wait time for message retransmits
#endif

void sip_tr_init()
{
	UCHAR i;
	SIP_TCB_HANDLE pTcb;

	pTcb = Sip_pReqTCB;
	for (i = 0; i < TR_TSIZE; i ++)
	{
		memset((PCHAR)pTcb, 0, SIP_TCB_LENGTH);
		pTcb ++;
	}
	pTcb = Sip_pRespTCB;
	for (i = 0; i < TR_TSIZE; i ++)
	{
		memset((PCHAR)pTcb, 0, SIP_TCB_LENGTH);
		pTcb ++;
	}
}

SIP_TCB_HANDLE sip_tr_new_req()
{
	UCHAR i;
	SIP_TCB_HANDLE pTcb;

	pTcb = Sip_pReqTCB;
	for (i = 0; i < TR_TSIZE; i ++)
	{
		if (pTcb->iState == TRS_FREE)
		{
			return pTcb;
		}
		pTcb ++;
	}

	UdpDebugString("No Free Req Transactions");
	return NULL;
}

void sip_tr_free(SIP_TCB_HANDLE pTcb)
{
	sip_debug("sip_tr_free", pTcb->iMethod);
	if (pTcb->pBranch)
	{
		free(pTcb->pBranch);
	}
	if (pTcb->pData)
	{
		free(pTcb->pData);
	}
	memset((PCHAR)pTcb, 0, SIP_TCB_LENGTH);
}

void sip_tr_free_lcb(SIP_LCB_HANDLE pLcb)
{
	UCHAR i;
	SIP_TCB_HANDLE pTcb;
	
	pTcb = Sip_pReqTCB;
	for (i = 0; i < TR_TSIZE; i ++)
	{
		if (pTcb->iState != TRS_FREE && pTcb->pLcb == pLcb)
		{
			pTcb->pLcb = NULL;
		}
		pTcb ++;
	}
	pTcb = Sip_pRespTCB;
	for (i = 0; i < TR_TSIZE; i ++)
	{
		if (pTcb->iState != TRS_FREE && pTcb->pLcb == pLcb)
		{
			pTcb->pLcb = NULL;
		}
		pTcb ++;
	}
}

void sip_tr_send(PCHAR pDstIP, USHORT sDstPort, BOOLEAN bSave)
{
	USHORT sLen;
#ifdef OEM_IP20
	PCHAR pTempSys;
#endif

//	TaskMiniRun();		// can not call outside of Ne2000Run();
	memcpy4(Sip_pCurTcb->pDstIP, pDstIP);
	Sip_pCurTcb->sDstPort = sDstPort;

	sLen = line_get_len();
	if (bSave)
	{
		if (Sip_pCurTcb->pData)	
		{
			free(Sip_pCurTcb->pData);
			Sip_pCurTcb->pData = NULL;
		}
		Sip_pCurTcb->pData = heap_save_str(Adapter_pUdpBuf);
	}
	// send out packet
	UdpSendTo(Sip_pSocket, sLen, Sip_pCurTcb->pDstIP, Sip_pCurTcb->sDstPort);

#ifdef OEM_IP20
	pTempSys = heap_save_str(Adapter_pUdpBuf);		//added for syslog sip messaging
	line_start(Adapter_pUdpBuf);
	line_add_str("PPP");
	line_add_short(111);
	line_add_char(' ');
	line_add_str(pTempSys);		
	
	UdpSend(_pSyslogSocket, strlen(Adapter_pUdpBuf)+1);
	
	free(pTempSys);
	pTempSys = NULL;

#endif


}

void sip_tr_send_req(PCHAR pDstIP, USHORT sDstPort, PCHAR pBranch, UCHAR iMethod, SIP_LCB_HANDLE pLcb)
{
	Sip_pCurTcb = sip_tr_new_req();
	if (Sip_pCurTcb)
	{
		Sip_pCurTcb->iState = TRS_TRYING;
		Sip_pCurTcb->iMethod = iMethod;
		Sip_pCurTcb->pLcb = pLcb;
		Sip_pCurTcb->pBranch = heap_save_str(pBranch);

		sip_tr_send(pDstIP, sDstPort, TRUE);
	}
	else
	{
		UdpSendTo(Sip_pSocket, line_get_len(), pDstIP, sDstPort);
	}
}

void sip_tr_send_resp(PCHAR pDstIP, USHORT sDstPort, USHORT sResp)
{
	BOOLEAN bSave;
	UCHAR iMethod;

	bSave = TRUE;
	iMethod = Sip_pCurTcb->iMethod;
	if (iMethod == SIP_REQ_INVITE)
	{
		if (sResp >= 200)
		{
			Sip_pCurTcb->iState = TRS_PROCEEDING;
			Sip_pCurTcb->iTimer0 = 0;
		}
	}
	else
	{
		if (sResp < 200)	return;
		if (iMethod == SIP_REQ_OPTIONS)
		{
			bSave = FALSE;
		}
		Sip_pCurTcb->iState = TRS_COMPLETED;
	}

	sip_tr_send(pDstIP, sDstPort, bSave);
}

void sip_tr_retry(SIP_TCB_HANDLE pTcb)
{
	PCHAR pMsg;

	sip_debug("sip_tr_retry", pTcb->iMethod);
	pMsg = pTcb->pData;
	if (pMsg)
	{
		strcpy(Adapter_pUdpBuf, pMsg);
		UdpSendTo(Sip_pSocket, strlen(pMsg), pTcb->pDstIP, pTcb->sDstPort);
	}
}

void sip_tr_timer()
{
	UCHAR i, iState;
	SIP_TCB_HANDLE pTcb;

	pTcb = Sip_pReqTCB;
	for (i = 0; i < TR_TSIZE; i ++, pTcb ++)
	{
		switch (pTcb->iState)
		{
		case TRS_TRYING:
			pTcb->iTimer1 ++;
			if (pTcb->iTimer1 > TR_TIMER_B)
			{
				// Timer B fires, inform TU of transmission error ...
				sip_handle_error(pTcb->pLcb, pTcb->iMethod);
				sip_tr_free(pTcb);
				break;
			}

			pTcb->iTimer0 ++;
			if (pTcb->iTimer0 > TR_TIMER_A)
			{
				if (pTcb->iMethod == SIP_REQ_INVITE && !pTcb->pLcb)
				{
					sip_tr_free(pTcb);
				}
				else
				{
					sip_tr_retry(pTcb);
					pTcb->iTimer0 = 0;
				}
			}
			break;

		case TRS_COMPLETED:
			pTcb->iTimer2 ++;
			if (pTcb->iTimer2 > TR_TIMER_D)
			{
				sip_tr_free(pTcb);
			}
			break;
		}
	}

	pTcb = Sip_pRespTCB;
	for (i = 0; i < TR_TSIZE; i ++, pTcb ++)
	{
		iState = pTcb->iState;
		if (iState == TRS_FREE)	continue;
		if (iState == TRS_COMPLETED)
		{
			pTcb->iTimer2 ++;
			if (pTcb->iTimer2 > TR_TIMER_D)
			{
				sip_tr_free(pTcb);
			}
		}
		else
		{
			if (pTcb->iMethod == SIP_REQ_INVITE)
			{
				if (iState == TRS_TRYING)
				{
					if (pTcb->b100rel)
					{
						pTcb->iTimer0 ++;
						if (pTcb->iTimer0 > TR_TIMER_A)
						{
							sip_tr_retry(pTcb);
							pTcb->iTimer0 = 0;
						}
					}
				}
				else
				{
					pTcb->iTimer1 ++;
					if (pTcb->iTimer1 > TR_TIMER_B)
					{
						sip_tr_free(pTcb);
						break;
					}

					pTcb->iTimer0 ++;
					if (pTcb->iTimer0 > TR_TIMER_A)
					{
						sip_tr_retry(pTcb);
						pTcb->iTimer0 = 0;
					}
				}
			}
		}
	}
}
/**********************************************************************
 ** Subscribe functions                                                **
 **********************************************************************
 */
void sip_sub_init()
{
	Sip_bSubscribe = FALSE;
	Sip_pSubReqUri = NULL;
	Sip_pSubCallId = NULL;
	Sip_pSubFromTag = NULL;
	Sip_pSubToTag = NULL;
	Sip_lSubSeq = 1;
	Sip_sSubTimer = 0;
	Sip_sSubTimeout = SIP_DEFAULT_SUB_EXPIRE;
	Sip_iSubAuthRetry = 0;
	Sip_pSubWWWAuth = NULL;
	Sip_pSubProxyAuth = NULL;
}

void sip_sub_free()
{
	if (Sip_pSubReqUri)		free(Sip_pSubReqUri);
	if (Sip_pSubCallId)		free(Sip_pSubCallId);
	if (Sip_pSubFromTag)	free(Sip_pSubFromTag);
	if (Sip_pSubToTag)		free(Sip_pSubToTag);
	if (Sip_pSubWWWAuth)	free(Sip_pSubWWWAuth);
	if (Sip_pSubProxyAuth)	free(Sip_pSubProxyAuth);
}

void sip_sub_failed()
{
	sip_sub_free();
	sip_sub_init();
	Sip_bSubscribe = OptionsGetByte(OPT_SUBSCRIBE_MWI)? TRUE:FALSE;
}

void sip_add_from_to(PCHAR pHeader, PCHAR pTag)
{
	line_add_str(pHeader);
	sip_add_local_uri(FALSE, FALSE);
	sip_add_token(_cTokenTag, pTag);
	line_add_crlf();
}

void sip_send_subscribe()
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	sip_debug(_cSend, SIP_REQ_SUBSCRIBE);

	sip_add_req_line(SIP_REQ_SUBSCRIBE, Sip_pSubReqUri);

	sip_new_branch(pBranch);
	sip_add_via(pBranch);

	sip_add_from_to(_cFrom, Sip_pSubFromTag);
	sip_add_from_to(_cTo, Sip_pSubToTag);
	
	sip_add_call_id(Sip_pSubCallId);
	sip_add_cseq(Sip_lSubSeq, SIP_REQ_SUBSCRIBE);
	sip_add_contact();
	sip_add_expires(Sip_sSubTimeout);
	sip_add_max_forwards();
	sip_add_user_agent();
	sip_add_proxy_require();
	sip_add_header(_cAuthorization, Sip_pSubWWWAuth);
	sip_add_header(_cProxy_Authorization, Sip_pSubProxyAuth);
	sip_add_header(_cEvent, "message-summary");
	sip_add_content_length(0);
	line_add_crlf();

	sip_tr_send_req(Sip_pServerIP, Sip_sServerPort, pBranch, SIP_REQ_SUBSCRIBE, NULL);
	Sip_lSubSeq ++;
}

void sip_sub_start()
{
	UCHAR pBuf[MAX_DNS_LEN+MAX_DNS_LEN];

	Sip_sSubTimer = 0;
	Sip_sSubTimeout = SIP_DEFAULT_SUB_EXPIRE;
	if (!Sip_pSubReqUri)
	{
		line_start(pBuf);
		line_add_str(_cTokenSip);
		line_add_options_str(OPT_USER_NUMBER);
		line_add_char('@');
		line_add_options_str(OPT_SIP_DOMAIN);
		Sip_pSubReqUri = heap_save_str(pBuf);
	}

	if (!Sip_pSubCallId)
	{
		Sip_pSubCallId = (char *)malloc(MAX_CALL_ID_LEN+1);
		sip_new_callid(Sip_pSubCallId);
	}
	if (!Sip_pSubFromTag)
	{
		Sip_pSubFromTag = (char *)malloc(MAX_TAG_LEN+1);
		sip_new_token(Sip_pSubFromTag, MAX_TAG_LEN);
	}
	sip_send_subscribe();
}

void sip_sub_timer()
{
	if (Sip_bSubscribe)
	{
		Sip_sSubTimer ++;
		if (Sip_sSubTimer >= Sip_sSubTimeout)
		{
			sip_sub_start();
		}
	}
}

/**********************************************************************
 ** LOGIN functions                                                  **
 **********************************************************************
 */
void sip_logon_init()
{
	Sip_iRegState = RS_FREE;
	Sip_sRegTimer = 0;
	Sip_sRegTimeout = 0;
	Sip_pRegWWWAuth = NULL;
	Sip_pRegProxyAuth = NULL;
	Sip_iRegAuthRetry = 0;
	Sip_iNatTimer = 0;
	Sip_lRegSeq = 0;
	Sip_pRegCallId[0] = 0;
	Sip_pRegFromTag[0] = 0;
}

void sip_logon_free()
{
	if (Sip_pRegWWWAuth)
	{
		free(Sip_pRegWWWAuth);
		Sip_pRegWWWAuth = NULL;
	}
	if (Sip_pRegProxyAuth)
	{
		free(Sip_pRegProxyAuth);
		Sip_pRegProxyAuth = NULL;
	}
}

void sip_logon_failed()
{
	sip_logon_free();
	Sip_sRegTimer = 0;
	Sip_iRegAuthRetry = 0;
	Sip_iRegState = RS_FREE;
	TaskUIHandler(UI_LOGON_FAILED, 0);
}

void sip_logon_ok()
{
	sip_logon_free();
	Sip_iRegState = RS_REGISTERED;
	Sip_sRegTimer = 0;
	Sip_iRegAuthRetry = 0;
	TaskUIHandler(UI_LOGON_OK, 0);
}

void sip_send_register()
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];

	sip_debug(_cSend, SIP_REQ_REGISTER);

	line_start(Adapter_pUdpBuf);
	line_add_str("REGISTER sip:");
	line_add_options_str(OPT_SIP_DOMAIN);
	line_add_str(" SIP/2.0\r\n");

	sip_new_branch(pBranch);
	sip_add_via(pBranch);

	sip_add_from_to(_cFrom, Sip_pRegFromTag);
	sip_add_from_to(_cTo, NULL);

	sip_add_call_id(Sip_pRegCallId);
	sip_add_cseq(Sip_lRegSeq, SIP_REQ_REGISTER);
	sip_add_contact();
	sip_add_expires(Sip_sRegTimeout);
	sip_add_max_forwards();
	sip_add_user_agent();
	sip_add_proxy_require();
	sip_add_header(_cAuthorization, Sip_pRegWWWAuth);
	sip_add_header(_cProxy_Authorization, Sip_pRegProxyAuth);
	sip_add_content_length(0);
	line_add_crlf();

	sip_tr_send_req(Sip_pServerIP, Sip_sServerPort, pBranch, SIP_REQ_REGISTER, NULL);
	Sip_lRegSeq ++;
}

void sip_logon_timer()
{
	UCHAR iNatTTL;
	USHORT sRegTTL;

	if (Sip_iRegState == RS_FREE)
	{
		Sip_sRegTimer ++;
		if(Sip_sRegTimer >= SIP_DEFAULT_REG_EXPIRE)
		{
			Sip_sRegTimer = 0;
			SipLogon();
		}
	}
	else if (Sip_iRegState == RS_REGISTERED)
	{
		sRegTTL = (Sip_sRegTimeout >> 2) + (Sip_sRegTimeout >> 1);
		Sip_sRegTimer ++;
		if(Sip_sRegTimer >= sRegTTL)
		{
			Sip_iRegState = RS_REFRESHING;
			Sip_sRegTimer = 0;
			Sip_sRegTimeout = OptionsGetShort(OPT_REGISTER_TTL);
			sip_send_register();
		}

		iNatTTL = OptionsGetByte(OPT_KEEP_ALIVE);
		if (iNatTTL)
		{
			Sip_iNatTimer ++;
			if (Sip_iNatTimer >= iNatTTL)
			{
				strcpy(Adapter_pUdpBuf, "\r\n\r\n");
				UdpSendTo(Sip_pSocket, 4, Sip_pServerIP, Sip_sServerPort);
				Sip_iNatTimer = 0;
			}
		}
	}
}

/**********************************************************************
 ** Line functions                                                **
 **********************************************************************
 */
void sip_ln_init()
{
	UCHAR i;
	SIP_LCB_HANDLE pLcb;

	pLcb = Sip_pLCB;
	for (i = 0; i < LCB_TSIZE; i ++)
	{
		memset((PCHAR)pLcb, 0, SIP_LCB_LENGTH);
		pLcb ++;
	}
}

void sip_ln_free()
{
	if (!Sip_pCurLcb)	return;
	
	UdpDebugString("sip_ln_free");
	sip_tr_free_lcb(Sip_pCurLcb);
	UdpClose(Sip_pCurLcb->pRtpSocket);

	if (Sip_pCurLcb->pInvUri)	free(Sip_pCurLcb->pInvUri);
	if (Sip_pCurLcb->pReqUri)	free(Sip_pCurLcb->pReqUri);
	if (Sip_pCurLcb->pBranch)	free(Sip_pCurLcb->pBranch);
	if (Sip_pCurLcb->pCallId)	free(Sip_pCurLcb->pCallId);
	if (Sip_pCurLcb->pFrom)		free(Sip_pCurLcb->pFrom);
	if (Sip_pCurLcb->pTo)		free(Sip_pCurLcb->pTo);
	if (Sip_pCurLcb->pFromTag)	free(Sip_pCurLcb->pFromTag);
	if (Sip_pCurLcb->pToTag)	free(Sip_pCurLcb->pToTag);
	if (Sip_pCurLcb->pRoute)	free(Sip_pCurLcb->pRoute);
	if (Sip_pCurLcb->pProxyAuth)	free(Sip_pCurLcb->pProxyAuth);
	if (Sip_pCurLcb->pWWWAuth)		free(Sip_pCurLcb->pWWWAuth);
	if (Sip_pCurLcb->pReferredBy)	free(Sip_pCurLcb->pReferredBy);
	if (Sip_pCurLcb->pReplaces)		free(Sip_pCurLcb->pReplaces);
	if (Sip_pCurLcb->pInvHeader)	free(Sip_pCurLcb->pInvHeader);

	memset((PCHAR)Sip_pCurLcb, 0, SIP_LCB_LENGTH);
}

SIP_LCB_HANDLE sip_ln_alt()
{
	UCHAR i;
	SIP_LCB_HANDLE pLcb;

	pLcb = Sip_pLCB;
	for (i = 0; i < LCB_TSIZE; i ++, pLcb ++)
	{
		if (pLcb != Sip_pActiveLcb)
		{
			return pLcb;
		}
	}
	return NULL;
}

void sip_show_curcid(SIP_LCB_HANDLE pLcb, BOOLEAN bShowLine0)
{
#ifdef OEM_IP20
	UCHAR pNumName[64];
#endif
	
#ifdef UI_4_LINES
	bShowLine0;
#ifdef OEM_IP20
	strcpy(pNumName, pLcb->pPeerNumber);
	if (strcmp(pLcb->pPeerName,""))		strcat(pNumName, "<");
	strcat(pNumName, pLcb->pPeerName);
	if (strcmp(pLcb->pPeerName,""))		strcat(pNumName, ">");
	TaskUIHandler(UI_CALL_DISPLAY_LINE2, (USHORT)pNumName);
#else
	TaskUIHandler(UI_CALL_DISPLAY_LINE1, (USHORT)pLcb->pPeerName);
	if (strcmp(pLcb->pPeerName, pLcb->pPeerNumber))
	{
		TaskUIHandler(UI_CALL_DISPLAY_LINE2, (USHORT)pLcb->pPeerNumber);
	}
#endif	
#else
	if (bShowLine0 && strcmp(pLcb->pPeerName, pLcb->pPeerNumber))
	{
		TaskUIHandler(UI_CALL_DISPLAY_LINE0, (USHORT)pLcb->pPeerName);
	}
	TaskUIHandler(UI_CALL_DISPLAY_LINE1, (USHORT)pLcb->pPeerNumber);
#endif
}

void sip_check_altline()
{
	Sip_pActiveLcb = sip_ln_alt();
	if (Sip_pActiveLcb->iState == CALL_STATE_RINGING)
	{
		TaskUIHandler(UI_CALL_RINGING, Sip_pActiveLcb->iVoiceCoder);
		sip_show_curcid(Sip_pActiveLcb, TRUE);
	}
	else if (Sip_pActiveLcb->iState == CALL_STATE_TALKING)
	{
		if (Sip_pActiveLcb->iXferPart == XFER_PART_TRANSFEROR)
		{
			Sip_pActiveLcb->iState = CALL_STATE_HANGUP;
		}
		else
		{
			TaskUIHandler(UI_CALL_HOLD, (USHORT)(&Sip_pActiveLcb->iVoiceCoder));
			sip_show_curcid(Sip_pActiveLcb, FALSE);
		}
	}
	else
	{
		TaskUIHandler(UI_CALL_FINISHED, 0);
	}
}

void sip_caller_failed(UCHAR iCause)
{
	if (Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEREE_NEW)
	{
		if (SipXfereeDone(503))
		{
			return;
		}
	}

	Sip_pCurLcb->iState = CALL_STATE_HANGUP;
	TaskUIHandler(UI_CALL_ENDED, iCause);
}

/**********************************************************************
 ** Misc functions                                                  **
 **********************************************************************
 */

void sip_handle_error(SIP_LCB_HANDLE pLcb, UCHAR iMethod)
{
	if (iMethod == SIP_REQ_REGISTER)
	{
		sip_logon_failed();
		return;
	}

	if (iMethod == SIP_REQ_SUBSCRIBE)
	{
		sip_sub_failed();
		return;
	}
	if (!pLcb)
	{
		return;
	}
	Sip_pCurLcb = pLcb;
	if (iMethod == SIP_REQ_INVITE)
	{
		if (Sip_pCurLcb == Sip_pActiveLcb)
		{
			if (Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEREE_NEW)
			{
				if (SipXfereeDone(480))
				{
					return;
				}
			}
			Sip_pCurLcb->iState = CALL_STATE_HANGUP;
			TaskUIHandler(UI_CALL_ENDED, CALL_END_MSG_TIMEOUT);
		}
		else
		{
			sip_ln_free();
		}
		return;
	}

	if (iMethod == SIP_REQ_CANCEL || iMethod == SIP_REQ_BYE || iMethod == SIP_REQ_NOTIFY)
	{
		if (Sip_pCurLcb == Sip_pActiveLcb)
		{
			Sip_pCurLcb->iState = CALL_STATE_HANGUP;
			TaskUIHandler(UI_CALL_ENDED, CALL_END_MSG_TIMEOUT);
		}
		else
		{
			sip_ln_free();
		}
	}
	else
	{
		if (iMethod == SIP_REQ_REFER)
		{
			if (Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEROR)
			{
				Sip_pCurLcb->iXferPart = XFER_PART_NONE;
				if (Sip_pCurLcb == Sip_pActiveLcb)
				{
					SipUnhold();
				}
			}
		}
	}
}

void sip_add_auth(PCHAR pUri, UCHAR iMethod)
{
	UCHAR pAuthID[MAX_AUTH_ID_LEN];
	UCHAR pAuthPin[MAX_AUTH_PIN_LEN];
	UCHAR pMethod[16];	

	OptionsGetString(pAuthID, OPT_AUTH_ID, MAX_AUTH_ID_LEN);
	OptionsGetString(pAuthPin, OPT_AUTH_PIN, MAX_AUTH_PIN_LEN);
	strcpy(pMethod, _cRequest[iMethod]);		// we need to copy because http_digest() and _cMethod are in different banks
	
	if (Sip_pCurLcb->pProxyAuth)
	{
		line_add_str(_cProxy_Authorization);
		TaskHttpDigest(line_get_buf(), Sip_pCurLcb->pProxyAuth, pUri, pAuthID, pAuthPin, pMethod);
		line_update_len();
		line_add_crlf();
	}
	if (Sip_pCurLcb->pWWWAuth)
	{
		line_add_str(_cAuthorization);
		TaskHttpDigest(line_get_buf(), Sip_pCurLcb->pWWWAuth, pUri, pAuthID, pAuthPin, pMethod);
		line_update_len();
		line_add_crlf();
	}
}

void sip_add_common()
{
	sip_add_header(_cRoute, Sip_pCurLcb->pRoute);
	sip_add_call_id(Sip_pCurLcb->pCallId);
	sip_add_contact();
	sip_add_max_forwards();
	sip_add_user_agent();
	sip_add_proxy_require();
	sip_add_header(_cFrom, Sip_pCurLcb->pFrom);
}

void sip_add_content_sdp(SIP_LCB_HANDLE pLcb)
{
	UCHAR iLen;
	USHORT sContentLen;
	UCHAR pBuf[6];
	PCHAR pLen;

	sip_add_content_type(_cAppSdp);
	line_add_str(_cContent_Length);
	line_add_str("     ");
	pLen = line_get_buf();
	line_add_crlf();
	line_add_crlf();
	// add SDP body
	sContentLen = sip_add_sdp(pLcb);
	itoa(sContentLen, pBuf, 10);
	iLen = strlen(pBuf);
	pLen -= iLen;
	memcpy(pLen, pBuf, iLen);
}

void sip_send_invite()
{
	PCHAR pUri;
	PCHAR pBranch;

	sip_debug(_cSend, SIP_REQ_INVITE);

	pUri = Sip_pCurLcb->pInvUri;
	sip_add_req_line(SIP_REQ_INVITE, pUri);

	if (Sip_pCurLcb->pBranch)	free(Sip_pCurLcb->pBranch);
	pBranch = (PCHAR)malloc((USHORT)(MAX_BRANCH_LEN+1));
	Sip_pCurLcb->pBranch = pBranch;
	sip_new_branch(pBranch);
	sip_add_via(pBranch);

	sip_add_common();
	if (Sip_pCurLcb->pTo)
	{
		sip_add_header(_cTo, Sip_pCurLcb->pTo);
	}
	else
	{
		sip_add_to(pUri, NULL);
	}
	sip_add_cseq(Sip_pCurLcb->lCurSeq, SIP_REQ_INVITE);
	Sip_pCurLcb->lInvSeq = Sip_pCurLcb->lCurSeq;
	Sip_pCurLcb->lCurSeq ++;

	if (Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEREE_NEW)
	{
		sip_add_header(_cReferredBy, Sip_pCurLcb->pReferredBy);
		sip_add_header(_cReplaces, Sip_pCurLcb->pReplaces);
	}
	sip_add_auth(pUri, SIP_REQ_INVITE);
	sip_add_supported();
	sip_add_allow();
	sip_add_session_expires(Sip_pCurLcb);
	sip_add_content_sdp(Sip_pCurLcb);
/*
	// test INVITE without SDP
	sip_add_content_length(0);
	line_add_crlf();
*/
	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_INVITE, Sip_pCurLcb);
	Sip_pCurLcb->pInvTcb = Sip_pCurTcb;
}

void sip_send_reinvite()
{
	if (Sip_pCurLcb->pInvUri)	free(Sip_pCurLcb->pInvUri);
	Sip_pCurLcb->pInvUri = heap_save_str(Sip_pCurLcb->pReqUri);
	Sip_pCurLcb->iSdpType = SDP_TYPE_REINVITE;
	sip_send_invite();
}

void sip_send_ack()
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	PCHAR pUri;

	sip_debug(_cSend, SIP_REQ_ACK);

	if (Sip_sRecvResp < 300)
	{
		pUri = Sip_pCurLcb->pReqUri;
		sip_add_req_line(SIP_REQ_ACK, pUri);
		sip_new_branch(pBranch);
		sip_add_via(pBranch);
	}
	else
	{
		pUri = Sip_pCurLcb->pInvUri;
		sip_add_req_line(SIP_REQ_ACK, pUri);
		sip_add_via(Sip_pCurLcb->pBranch);
	}
	sip_add_common();
	sip_add_header(_cTo, Sip_pCurLcb->pTo);
	sip_add_cseq(Sip_pCurLcb->lInvSeq, SIP_REQ_ACK);
	sip_add_auth(pUri, SIP_REQ_ACK);
	sip_add_content_length(0);
	line_add_crlf();
/*	
	//test ACK with SDP
	if (Sip_sRecvResp < 300)
	{
		sip_add_content_sdp(Sip_pCurLcb);
	}
*/
	sip_tr_send(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, TRUE);
}

void sip_send_bye()
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	PCHAR pUri;

	sip_debug(_cSend, SIP_REQ_BYE);

	pUri = Sip_pCurLcb->pReqUri;
	sip_add_req_line(SIP_REQ_BYE, pUri);
	sip_new_branch(pBranch);
	sip_add_via(pBranch);
	sip_add_common();
	sip_add_header(_cTo, Sip_pCurLcb->pTo);
	sip_add_cseq(Sip_pCurLcb->lCurSeq, SIP_REQ_BYE);
	Sip_pCurLcb->lCurSeq ++;
	sip_add_content_length(0);
	sip_add_auth(pUri, SIP_REQ_BYE);
	line_add_crlf();

	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_BYE, Sip_pCurLcb);
}

void sip_send_cancel()
{
	PCHAR pBranch;
	PCHAR pUri;

	sip_debug(_cSend, SIP_REQ_CANCEL);

	pUri = Sip_pCurLcb->pInvUri;
	sip_add_req_line(SIP_REQ_CANCEL, pUri);
	pBranch = Sip_pCurLcb->pBranch;
	sip_add_via(pBranch);
	sip_add_common();
	sip_add_to(pUri, NULL);
	sip_add_cseq(Sip_pCurLcb->lInvSeq, SIP_REQ_CANCEL);
	sip_add_content_length(0);
	sip_add_auth(pUri, SIP_REQ_CANCEL);
	line_add_crlf();

	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_CANCEL, Sip_pCurLcb);
}

void sip_send_info(UCHAR iKey)
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	UCHAR pBuf[32];
	PCHAR pUri;
	USHORT sLen;

	sip_debug(_cSend, SIP_REQ_INFO);
	// compose INFO body
	line_start(pBuf);
	line_add_str("Signal=");
	line_add_char(iKey);
	line_add_crlf();
	line_add_str("Duration=200");
	line_add_crlf();

	sLen = line_get_len();
	// compose INFO msg
	pUri = Sip_pCurLcb->pReqUri;
	sip_add_req_line(SIP_REQ_INFO, pUri);
	sip_new_branch(pBranch);
	sip_add_via(pBranch);
	sip_add_common();
	sip_add_header(_cTo, Sip_pCurLcb->pTo);
	sip_add_cseq(Sip_pCurLcb->lCurSeq, SIP_REQ_INFO);
	Sip_pCurLcb->lCurSeq ++;
	sip_add_auth(pUri, SIP_REQ_INFO);
	sip_add_content_type(_cAppDtmf);
	sip_add_content_length(sLen);
	line_add_crlf();
	line_add_str(pBuf);

	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_INFO, Sip_pCurLcb);
}

void sip_send_notify(USHORT sResp)
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	UCHAR pBuf[64];
	PCHAR pUri;
	USHORT sLen;

	sip_debug(_cSend, SIP_REQ_NOTIFY);
	// compose NOTIFY body
	line_start(pBuf);
	sip_add_status_line(sResp);
	sLen = line_get_len();
	// compose NOTIFY msg
	pUri = Sip_pCurLcb->pReqUri;
	sip_add_req_line(SIP_REQ_NOTIFY, pUri);
	sip_new_branch(pBranch);
	sip_add_via(pBranch);
	sip_add_common();
	sip_add_header(_cTo, Sip_pCurLcb->pTo);
	sip_add_cseq(Sip_pCurLcb->lCurSeq, SIP_REQ_NOTIFY);
	Sip_pCurLcb->lCurSeq ++;
	sip_add_auth(pUri, SIP_REQ_NOTIFY);
	sip_add_header(_cEvent, "refer");
	sip_add_content_type(_cMsgFrag);
	sip_add_content_length(sLen);
	line_add_crlf();
	line_add_str(pBuf);

	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_NOTIFY, Sip_pCurLcb);
}

void sip_send_prack()
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	PCHAR pUri;
	UCHAR pSeq[128];

	sip_debug(_cSend, SIP_REQ_PRACK);

	strcpy(pSeq, Sip_pRecvCSeq);

	pUri = Sip_pCurLcb->pReqUri;
	sip_add_req_line(SIP_REQ_PRACK, pUri);
	sip_new_branch(pBranch);
	sip_add_via(pBranch);
	sip_add_common();
	sip_add_header(_cTo, Sip_pCurLcb->pTo);
	sip_add_cseq(Sip_pCurLcb->lCurSeq, SIP_REQ_PRACK);
	Sip_pCurLcb->lCurSeq ++;
	sip_add_content_length(0);
	sip_add_auth(pUri, SIP_REQ_PRACK);

	// add RACK
	line_add_str(_cRAck);
	line_add_long(Sip_pCurLcb->lRseq);
	line_add_char(' ');
	line_add_str(pSeq);
	line_add_crlf();
	line_add_crlf();

	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_PRACK, Sip_pCurLcb);
}

void sip_send_refer(PCHAR pReferTo)
{
	UCHAR pBranch[MAX_BRANCH_LEN+1];
	PCHAR pUri;

	sip_debug(_cSend, SIP_REQ_REFER);
	pUri = Sip_pCurLcb->pReqUri;
	sip_add_req_line(SIP_REQ_REFER, pUri);
	sip_new_branch(pBranch);
	sip_add_via(pBranch);
	sip_add_common();
	sip_add_header(_cTo, Sip_pCurLcb->pTo);
	sip_add_cseq(Sip_pCurLcb->lCurSeq, SIP_REQ_REFER);
	Sip_pCurLcb->lCurSeq ++;
	// add refer-to
	line_add_str_with_char(_cReferTo, '<');
	line_add_str_with_char(pReferTo, '>');
	line_add_crlf();
	sip_add_referredby();
	sip_add_content_length(0);
	sip_add_auth(pUri, SIP_REQ_REFER);
	line_add_crlf();

	sip_tr_send_req(Sip_pCurLcb->pDstIP, Sip_pCurLcb->sDstPort, pBranch, SIP_REQ_REFER, Sip_pCurLcb);
}

void sip_send_invresp(USHORT sResp)
{ 
	sip_debug(_cSend, sResp);
	Sip_pCurTcb = Sip_pCurLcb->pInvTcb;

	line_start(Adapter_pUdpBuf);
	sip_add_status_line(sResp);
	
	line_add_str(Sip_pCurLcb->pInvHeader);
	free(Sip_pCurLcb->pInvHeader);
	Sip_pCurLcb->pInvHeader = NULL;

	sip_add_header(_cFrom, Sip_pCurLcb->pTo);
	sip_add_header(_cTo, Sip_pCurLcb->pFrom);
	sip_add_call_id(Sip_pCurLcb->pCallId);
	sip_add_max_forwards();
	sip_add_user_agent();
	if (sResp == 302)
	{
		sip_add_fwdcontact();
	}
	else
	{
		sip_add_contact();
	}
	if (sResp == 200)
	{
		sip_add_session_expires(Sip_pCurLcb);
		sip_add_supported();
		sip_add_allow();
		sip_add_content_sdp(Sip_pCurLcb);
	}
	else
	{
		sip_add_content_length(0);
		line_add_crlf();
	}
	sip_tr_send_resp(Sip_pCurLcb->pRespDstIP, Sip_pCurLcb->sRespDstPort, sResp);
}
/**********************************************************************
 ** API functions                                                    **
 **********************************************************************
 */
void SipInit()
{
	Sip_pActiveLcb = Sip_pLCB;
	Sip_pCurLcb = Sip_pActiveLcb;

	sip_logon_init();
	sip_sub_init();
	sip_tr_init();
	sip_ln_init();

	memset(Sip_pNatIP, 0, IP_ALEN);
	Sip_sNatPort = 0;
	memset(Sip_pServerIP, 0, IP_ALEN);
	Sip_sServerPort = 0;
	Sip_pSocket = NULL;
	Sip_sRtpPort = 0;
	Sys_bRegister = FALSE;

	Rtp_pData = (PCHAR)(Adapter_pUdpBuf + RTP_DATA);
}

void SipStartup()
{
	if (OptionsGetByte(OPT_NAT_TRAVERSAL) == NAT_TRAVERSAL_NATIP)
	{
		OptionsGetBuf(Sip_pNatIP, OPT_NAT_IP, IP_ALEN);
	}
	else
	{
		memcpy4(Sip_pNatIP, Sys_pIpAddress);
	}
	Sip_sNatPort = OptionsGetShort(OPT_LOCAL_PORT);
	memset(Sip_pServerIP, 0, IP_ALEN);
	Sip_sServerPort = OptionsGetShort(OPT_SIP_PORT);
	if (Sip_pSocket)
	{
		UdpClose(Sip_pSocket);
	}
	Sip_pSocket = UdpListen(Sip_sNatPort, SIPR_BANK_OFFSET, (USHORT)SipRun);
	Sip_sRtpPort = OptionsGetShort(OPT_RTP_PORT);

	Sip_bSubscribe = OptionsGetByte(OPT_SUBSCRIBE_MWI)? TRUE:FALSE;
	Sys_bRegister = OptionsGetByte(OPT_REGISTER)? TRUE:FALSE;
	Sip_iRegState = RS_FREE;
	Sip_lRegSeq = rand();
	sip_new_callid(Sip_pRegCallId);
	sip_new_token(Sip_pRegFromTag, MAX_TAG_LEN);
}

void SipLogon()
{
	Sip_sRegTimer = 0;
	Sip_sRegTimeout = OptionsGetShort(OPT_REGISTER_TTL);

	if (OptionsGetByte(OPT_NAT_TRAVERSAL) == NAT_TRAVERSAL_STUN)
	{
//		UdpDebugString("Stun before logon...");
		TaskStunMap(STUN_TASK_LOGON, Sip_pSocket);
	}
	else
	{
		SipLogonStep1();
	}
}

void SipLogonStep1()
{
	UCHAR pSrvDns[MAX_DNS_LEN+10];
	PCHAR pDns;
	UCHAR pDstIpPort[IP_ALEN+2];

	pDns = (PCHAR)(pSrvDns + 10);
	if (Sys_bRegister)
	{
		UdpDebugString("Logon Step1");
		Sip_iRegState = RS_REGISTERING;
		OptionsGetString(pDns, OPT_SIP_PROXY, MAX_DNS_LEN);
		if (strlen(pDns))
		{
			if (str2ip(pDns, pDstIpPort))
			{
				USHORT2PCHAR(0, (PCHAR)(pDstIpPort + IP_ALEN));
				SipLogonStep2(pDstIpPort);
			}
			else
			{
				if (OptionsGetByte(OPT_DNS_SRV))
				{
					memcpy(pSrvDns, _cSipUdp, 10);
					TaskDnsQuery(pSrvDns, DNS_TYPE_SRV, PROTOCOL_BANK_OFFSET, (USHORT)SipLogonStep2);
				}
				else
				{
					TaskDnsQuery(pDns, DNS_TYPE_A, PROTOCOL_BANK_OFFSET, (USHORT)SipLogonStep2);
				}
			}
		}
		else
		{
			sip_logon_failed();
		}
	}
	else
	{
		sip_logon_ok();
	}
}

void SipLogonStep2(PCHAR pServerIP)
{
	USHORT sPort;

	if (pServerIP)
	{
		UdpDebugString("Logon Step2");
		memcpy4(Sip_pServerIP, pServerIP);
		sPort = PCHAR2USHORT((PCHAR)(pServerIP + IP_ALEN));
		if (sPort)
		{
			Sip_sServerPort = sPort;
		}
		sip_send_register();
		if (Sip_bSubscribe)
		{
			sip_sub_start();
		}
	}
	else
	{
		sip_logon_failed();
	}
}

void sip_new_nameuri(PCHAR pDst, PCHAR pName)
{
	strcpy(pDst, _cTokenSip);
	strcat(pDst, pName);
	strcat(pDst, "@");
	OptionsGetString((PCHAR)(pDst + strlen(pDst)), OPT_SIP_DOMAIN, MAX_DNS_LEN);
}

void sip_star2dot(PCHAR pBuf)
{
	PCHAR p;

	p = pBuf;
	while (*p)
	{
		if (*p == '*')	*p = '.';
		p ++;
	}
}

void SipCallStep1(PCHAR pCalledUri)
{
	PCHAR p;
	PCHAR pDomain;
	PCHAR pName;
	UCHAR iLen;
	UCHAR pDstIpPort[IP_ALEN+2];	
	UCHAR pSipProxy[MAX_DNS_LEN];
	UCHAR pSipDomain[MAX_DNS_LEN];
	UCHAR pBuf[128];
	USHORT sDstPort;
	BOOLEAN bThroughProxy;

	Sip_pCurLcb->iState = CALL_STATE_CALLING;

	iLen = strlen(pCalledUri);
	if (!iLen)
	{
		sip_caller_failed(CALL_END_EMPTY_NUMBER);
		return;
	}

	sDstPort = SIP_SERVER_PORT;
	p = strchr(pCalledUri, ':');
	if (p)
	{
		*p = 0;
		p ++;
		sDstPort = atoi(p, 10);
	}

	p = strchr(pCalledUri, '@');
	if (p)
	{
		*p = 0;
		p ++;
		pDomain = p;
		pName = pCalledUri;
	}
	else
	{
		if (str2ip(pCalledUri, pDstIpPort) && IsValidIP(pDstIpPort))
		{
			pDomain = pCalledUri;
			sip_star2dot(pDomain);
			pName = NULL;
		}
		else
		{
			pName = pCalledUri;
			pDomain = NULL;
		}
	}

	if (pName)	p = pName;
	else		p = pDomain;
	if (strlen(p) < MAX_USER_NAME_LEN)
	{
		strcpy(Sip_pCurLcb->pPeerName, p);
	}
	else
	{
		strcpy(Sip_pCurLcb->pPeerName, "NameTooLong");
	}
	strcpy(Sip_pCurLcb->pPeerNumber, Sip_pCurLcb->pPeerName);

	line_start(pBuf);
	line_add_str(_cTokenSip);
	if (pName)
	{
		line_add_str_with_char(pName, '@');
	}

	bThroughProxy = FALSE;
	OptionsGetString(pSipDomain, OPT_SIP_DOMAIN, MAX_DNS_LEN);
	OptionsGetString(pSipProxy, OPT_SIP_PROXY, MAX_DNS_LEN);
	if (pDomain)
	{
		if (!strcmp(pDomain, pSipProxy))
		{
			bThroughProxy = TRUE;
		}
		if (!strcmp(pDomain, pSipDomain))
		{
			bThroughProxy = TRUE;
		}
		line_add_str(pDomain);
	}
	else
	{
		if (strlen(pSipProxy) && strlen(pSipDomain))
		{
			bThroughProxy = TRUE;
			line_add_str(pSipDomain);
			pDomain = pSipProxy;
		}
		else
		{
			sip_caller_failed(CALL_END_WRONG_NUMBER);
			return;
		}
	}
	if (Sip_pCurLcb->pReqUri)	free(Sip_pCurLcb->pReqUri);
	Sip_pCurLcb->pReqUri = heap_save_str(pBuf);
	if (Sip_pCurLcb->pInvUri)	free(Sip_pCurLcb->pInvUri);
	Sip_pCurLcb->pInvUri = heap_save_str(pBuf);

	if (bThroughProxy)
	{
	
		Sip_pCurLcb->sDstPort = Sip_sServerPort;
	}
	else
	{
		Sip_pCurLcb->sDstPort = sDstPort;
	}
	USHORT2PCHAR(0, (PCHAR)(pDstIpPort + IP_ALEN));
	if (str2ip(pDomain, pDstIpPort))
	{
		SipCallStep2(pDstIpPort);
	}
	else
	{
		if (bThroughProxy && Sys_bRegister && Sip_iRegState != RS_FREE)
		{
			memcpy4(pDstIpPort, Sip_pServerIP);
			SipCallStep2(pDstIpPort);
		}
		else
		{
			if (OptionsGetByte(OPT_DNS_SRV))
			{
				strcpy(pBuf, _cSipUdp);
				strcat(pBuf, pDomain);
				TaskDnsQuery(pBuf, DNS_TYPE_SRV, PROTOCOL_BANK_OFFSET, (USHORT)SipCallStep2);
			}
			else
			{
				TaskDnsQuery(pDomain, DNS_TYPE_A, PROTOCOL_BANK_OFFSET, (USHORT)SipCallStep2);
			}
		}
	}
}

void sip_new_from()
{
	UCHAR pBuf[128];
	UCHAR pTag[MAX_TAG_LEN+1];

	line_start(pBuf);
	sip_add_local_uri(!Sys_bRegister, FALSE);
	sip_new_token(pTag, MAX_TAG_LEN);
	sip_add_token(_cTokenTag, pTag);
	free(Sip_pCurLcb->pFrom);
	Sip_pCurLcb->pFrom = heap_save_str(pBuf);
	free(Sip_pCurLcb->pFromTag);
	Sip_pCurLcb->pFromTag = heap_save_str(pTag);
}

void sip_modify_rtpport()
{
	if (OptionsGetByte(OPT_NAT_TRAVERSAL) != NAT_TRAVERSAL_NATIP)
	{
		if (Sip_sRtpPort >= 65534)
		{
			Sip_sRtpPort = OptionsGetShort(OPT_RTP_PORT);
		}
		else
		{
			Sip_sRtpPort += 2;
		}
	}
}

void SipCallStep3()
{
	Sip_pCurLcb->bCallee = FALSE;
	Sip_pCurLcb->lCurSeq = rand();
	Sip_pCurLcb->sSessionExpires = SIP_DEFAULT_SESSION_EXPIRE;
	Sip_pCurLcb->sMinSE = SIP_DEFAULT_MIN_SE;
	Sip_pCurLcb->iRefresher = SIP_REFRESHER_UAC;
	Sip_pCurLcb->pCallId = malloc((USHORT)(MAX_CALL_ID_LEN + 1));
	sip_new_callid(Sip_pCurLcb->pCallId);
	sip_new_from();

	sip_new_random(Sip_pCurLcb->pSdpSessionId, 8);
	Sip_pCurLcb->lSdpVersion = Sys_lCurrentTime; 
	Sip_pCurLcb->iLocalMode = AUDIO_MODE_SENDRECV;
	Sip_pCurLcb->iRemoteMode = AUDIO_MODE_INACTIVE;
	Sip_pCurLcb->iVoiceCoder = VOICE_CODER_NONE;
	Sip_pCurLcb->bVad = OptionsGetByte(OPT_VAD);
	Sip_pCurLcb->iCoderPayload = 255;
	Sip_pCurLcb->iDtmfPayload = 255;
	rand_array(Sip_pCurLcb->pRtpSSRC, RTP_SSRC_LEN);
	Sip_pCurLcb->sRtpSeq = P_MAKEWORD(rand(), rand());
	Sip_pCurLcb->lRtpTimeStamp = 0;
	Sip_pCurLcb->lRtpOffset = Sys_lTicker;
	Sip_pCurLcb->bVoiceStart = TRUE;
	Sip_pCurLcb->iSdpType = SDP_TYPE_OFFER;
	Sip_pCurLcb->sRtpPort = Sip_sRtpPort;
	sip_modify_rtpport();
	Sip_pCurLcb->pRtpSocket = UdpListen(Sip_pCurLcb->sRtpPort, SIPR_BANK_OFFSET, (USHORT)RtpRun);

	if (OptionsGetByte(OPT_NAT_TRAVERSAL) == NAT_TRAVERSAL_STUN)
	{
//		UdpDebugString("Stun before call...");
		TaskStunMap(STUN_TASK_CALL, Sip_pCurLcb->pRtpSocket);
	}
	else
	{
		sip_send_invite();
	}
}

void SipCallStep2(PCHAR pServerIP)
{
	USHORT sPort;

	if (Sip_pActiveLcb->iState != CALL_STATE_CALLING)	return;
	Sip_pCurLcb = Sip_pActiveLcb;

	if (!pServerIP)	
	{
		sip_caller_failed(CALL_END_BAD_SERVERIP);
		return;
	}
	sPort = PCHAR2USHORT((PCHAR)(pServerIP + IP_ALEN));
	if (sPort)
	{
		Sip_pCurLcb->sDstPort = sPort;
	}
	memcpy4(Sip_pCurLcb->pDstIP, pServerIP);
	Sip_pCurLcb->iState = CALL_STATE_CALLING2;
	SipCallStep3();
}

// Answer after 180 ringing has already been sent out
void SipAnswer()
{
	Sip_pCurLcb = Sip_pActiveLcb;
#ifdef OEM_IP20
#ifdef __ADSL
	Sip_pCurLcb->brtpIpPrivate = IsPrivateIP(Sip_pCurLcb->pRtpDstIP);	
#endif					
#endif
	Sip_pCurLcb->iState = CALL_STATE_TALKING;
	TaskUIHandler(UI_CALL_CONNECTED, (USHORT)(&Sip_pCurLcb->iVoiceCoder));
	sip_show_curcid(Sip_pCurLcb, FALSE);
	TaskCallLogAdd(Sip_pCurLcb->pPeerName, Sip_pCurLcb->pPeerNumber, CALL_LOG_TYPE_RECVED);

	if (OptionsGetByte(OPT_NAT_TRAVERSAL) == NAT_TRAVERSAL_STUN)
	{
//		UdpDebugString("Stun before answer...");
		TaskStunMap(STUN_TASK_ANSWER, Sip_pCurLcb->pRtpSocket);
	}
	else
	{
		sip_send_invresp(200);
	}
}

void SipDial()
{
	Sip_pActiveLcb->iState = CALL_STATE_DIALING;
	TaskUIHandler(UI_CALL_DIAL, 0);
}

void sip_unhold_call()
{
	Sip_pCurLcb->bHolding = TRUE;
	Sip_pCurLcb->iLocalMode |= AUDIO_MODE_RECVONLY;
	sip_send_reinvite();
//	TaskUIHandler(UI_CALL_CONNECTED, (USHORT)(&Sip_pCurLcb->iVoiceCoder));
//	sip_show_curcid(Sip_pCurLcb, FALSE);
}

void sip_hold_call()
{
	Sip_pCurLcb->bHolding = TRUE;
	Sip_pCurLcb->iLocalMode &= ~AUDIO_MODE_RECVONLY;
	sip_send_reinvite();
	TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_CALL_ONHOLD);
//	TaskUIHandler(UI_CALL_HOLD, (USHORT)(&Sip_pCurLcb->iVoiceCoder));
//	sip_show_curcid(Sip_pCurLcb, FALSE);
}

void SipHold()
{
	if (Sip_pActiveLcb->iState == CALL_STATE_TALKING && !Sip_pActiveLcb->bHolding)
	{
		Sip_pCurLcb = Sip_pActiveLcb;
		if (Sip_pCurLcb->iLocalMode & AUDIO_MODE_RECVONLY)
		{
			sip_hold_call();
		}
		else
		{
			sip_unhold_call();
		}
	}
}

void SipUnhold()
{
	if (Sip_pActiveLcb->iState == CALL_STATE_TALKING)
	{
		Sip_pCurLcb = Sip_pActiveLcb;
		if (!(Sip_pCurLcb->iLocalMode & AUDIO_MODE_RECVONLY))
		{
			sip_unhold_call();
		}
	}
}

void SipCallCanceled()
{
	UCHAR iState;

	iState = Sip_pCurLcb->iState;
	if (iState == CALL_STATE_RINGING)
	{
		sip_send_invresp(487);
		Sip_pCurLcb->iState = CALL_STATE_HANGUP;
		TaskCallLogAdd(Sip_pCurLcb->pPeerName, Sip_pCurLcb->pPeerNumber, CALL_LOG_TYPE_MISSED);
#ifdef OEM_IP20
		voice_message = 0;
#endif
		if (Sip_pCurLcb == Sip_pActiveLcb)
		{
			sip_check_altline();
		}
		else
		{
			TaskUIHandler(UI_CALL_TIMER, (USHORT)&Sip_pActiveLcb->ct);
			sip_show_curcid(Sip_pActiveLcb, FALSE);
		}
	}
	else if (iState == CALL_STATE_TALKING)
	{
		if (Sip_pCurLcb == Sip_pActiveLcb)
		{
			Sip_pCurLcb->iState = CALL_STATE_HANGUP;
			Sip_pCurLcb->iTimer = 0;
			TaskUIHandler(UI_CALL_ENDED, CALL_END_PEER_HANGUP);
		}
		else
		{
			sip_ln_free();
		}
	}
	else if (iState == CALL_STATE_HANGUP && Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEROR)
	{
		sip_ln_free();
	}
}

void SipHandsetUp(BOOLEAN bMemCall)
{
	UCHAR iState;

	iState = Sip_pActiveLcb->iState;
	Sip_pCurLcb = Sip_pActiveLcb;

	if (iState == CALL_STATE_RINGING)
	{
		SipAnswer();
	}
	else if (iState == CALL_STATE_FREE)
	{
		SipDial();
		if (bMemCall)
		{
			Sip_pActiveLcb->bMemCall = TRUE;
		}
#ifdef OEM_IP20
		else if (hotline)
		{
			OptionsGetString(Sip_pActiveLcb->pPeerNumber, OPT_HOTLINE_NUMBER, MAX_USER_NUMBER_LEN);
			if (strlen(Sip_pActiveLcb->pPeerNumber))
			{
				TaskUIHandler(UI_CALL_DISPLAY_LINE1, (USHORT)Sip_pActiveLcb->pPeerNumber);
				Sip_pActiveLcb->bMemCall = TRUE;
			}
		}
#else
		else
		{
			OptionsGetString(Sip_pActiveLcb->pPeerNumber, OPT_HOTLINE_NUMBER, MAX_USER_NUMBER_LEN);
			if (strlen(Sip_pActiveLcb->pPeerNumber))
			{
				TaskUIHandler(UI_CALL_DISPLAY_LINE1, (USHORT)Sip_pActiveLcb->pPeerNumber);
				Sip_pActiveLcb->bMemCall = TRUE;
			}
		}
#endif
	}
	else if (iState == CALL_STATE_TALKING)
	{
		sip_unhold_call();
	}
}

void SipHandsetDown()
{
	UCHAR iState;

	iState = Sip_pActiveLcb->iState;
	if (iState == CALL_STATE_RINGING)
	{
		return;
	}

	Sip_pCurLcb = Sip_pActiveLcb;
	if (iState == CALL_STATE_TALKING)
	{
		if (!(Sip_pCurLcb->iLocalMode & AUDIO_MODE_RECVONLY))
		{
			return;
		}
		else
		{
			sip_send_bye();
			Sip_pCurLcb->iState = CALL_STATE_HANGUP;
		}
	}
#ifdef OEM_IP20
	else if (iState == CALL_STATE_CALLING2 && TransFlagResp == 0)
	{
		sip_send_cancel();
		Sip_pCurLcb->iState = CALL_STATE_HANGUP;
	}
#else
	else if (iState == CALL_STATE_CALLING2)
	{
		if (Sip_pCurLcb->bEarlyDlg)
		{
			sip_send_cancel();
			Sip_pCurLcb->iState = CALL_STATE_HANGUP;
		}
		else
		{
			sip_tr_free(Sip_pCurLcb->pInvTcb);
			sip_ln_free();
		}
	}
#endif
	else if (iState != CALL_STATE_FREE)
	{
		sip_ln_free();
	}

	sip_check_altline();
}

void SipFlash()
{
	UCHAR iState;

	Sip_pCurLcb = Sip_pActiveLcb;
	iState = Sip_pCurLcb->iState;
//	if (iState == CALL_STATE_FREE)		return;
	if (iState == CALL_STATE_RINGING)	return;

	if (iState == CALL_STATE_DIALING || iState == CALL_STATE_CALLING || iState == CALL_STATE_HANGUP)
	{
		// cancel current call
		sip_ln_free();
	}
	else if (iState == CALL_STATE_CALLING2)
	{
		// cancel current call
		sip_send_cancel();
		Sip_pCurLcb->iState = CALL_STATE_HANGUP;
	}
	else if (iState == CALL_STATE_TALKING)
	{
		// hold current call
		if (Sip_pCurLcb->iLocalMode & AUDIO_MODE_RECVONLY)
		{
			sip_hold_call();
		}
	}

	// Active another call session
	Sip_pActiveLcb = sip_ln_alt();
	Sip_pCurLcb = Sip_pActiveLcb;
	iState = Sip_pCurLcb->iState;
	if (iState == CALL_STATE_FREE)
	{
		SipDial();
	}
	else if (iState == CALL_STATE_TALKING)
	{
		sip_unhold_call();
	}
	else if (iState == CALL_STATE_RINGING)
	{
		SipAnswer();
	}
	else if (iState == CALL_STATE_HANGUP)
	{
		TaskUIHandler(UI_CALL_FINISHED, 0);
	}
}

#ifdef OEM_IP20

void SipTransfer(PCHAR pNumber)
{
	SIP_LCB_HANDLE pAltLcb;
	UCHAR pBuf[256];
	PCHAR pTempBuf;
	PCHAR pTemp;
	USHORT s;

	pAltLcb = sip_ln_alt();	
	if (Sip_pActiveLcb->iState != CALL_STATE_TALKING)
	{	
		if (Sip_pActiveLcb->iState == CALL_STATE_DIALING)	SipCall(pNumber);		
		TransFlagResp = 1;
		if (pAltLcb->iState == CALL_STATE_TALKING)
		{
 			TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_CALL_XFER);
			line_start(pBuf);
			if (Sys_bRegister)
			{
				sip_new_nameuri(pBuf, Sip_pActiveLcb->pPeerNumber);
				line_update_len();
			}
			else
			{
				line_add_str(_cTokenSip);
				line_add_str(Sip_pActiveLcb->pPeerNumber);
			}
			line_add_str("?Replaces=");
			pTemp = line_get_buf();
	
			pTempBuf = Adapter_pUdpBuf;

			strcpy(pTempBuf, Sip_pActiveLcb->pCallId);
			strcat(pTempBuf, ";to-tag=");
			strcat(pTempBuf, Sip_pActiveLcb->pToTag);
			strcat(pTempBuf, ";from-tag=");
			strcat(pTempBuf, Sip_pActiveLcb->pFromTag);
			sip_escape_str(pTemp, pTempBuf);
			Sip_pCurLcb = pAltLcb;
			Sip_pCurLcb->iXferPart = XFER_PART_TRANSFEROR;
			Sip_pActiveLcb->iXferPart = XFER_PART_TRANSFEROR;
		
			for (s = 0; s < 1000; s ++)
			{
				delay1ms();
			}
			sip_send_refer(pBuf);	
		}
		else
		{
			TaskUIHandler(UI_CALL_DISPLAY_IDS2, IDS_XFER_FAILED);
		}
	}
	else if (Sip_pActiveLcb->iState == CALL_STATE_TALKING && pAltLcb->iState == CALL_STATE_TALKING)
	{
		TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_CALL_XFER);
		line_start(pBuf);
		if (Sys_bRegister)
		{
			sip_new_nameuri(pBuf, Sip_pActiveLcb->pPeerNumber);
			line_update_len();
		}
		else
		{
			line_add_str(_cTokenSip);
			line_add_str(Sip_pActiveLcb->pPeerNumber);
		}
		line_add_str("?Replaces=");
		pTemp = line_get_buf();

		pTempBuf = Adapter_pUdpBuf;

		strcpy(pTempBuf, Sip_pActiveLcb->pCallId);
		strcat(pTempBuf, ";to-tag=");
		strcat(pTempBuf, Sip_pActiveLcb->pToTag);
		strcat(pTempBuf, ";from-tag=");
		strcat(pTempBuf, Sip_pActiveLcb->pFromTag);
		sip_escape_str(pTemp, pTempBuf);
		
		Sip_pCurLcb = pAltLcb;
		Sip_pCurLcb->iXferPart = XFER_PART_TRANSFEROR;
		Sip_pActiveLcb->iXferPart = XFER_PART_TRANSFEROR;
		sip_send_refer(pBuf);
	}
}

#else

void SipBlindTransfer(PCHAR pNumber)
{
	SIP_LCB_HANDLE pAltLcb;
	UCHAR pBuf[256];

	pAltLcb = sip_ln_alt();
	if (strlen(pNumber))
	{
		if (pAltLcb->iState == CALL_STATE_TALKING)
		{
//			UdpDebugString("Blind transfer");
			TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_CALL_XFER);
			Sip_pCurLcb = Sip_pActiveLcb;
			sip_ln_free();
			Sip_pActiveLcb = pAltLcb;
			Sip_pCurLcb = pAltLcb;
			Sip_pCurLcb->iXferPart = XFER_PART_TRANSFEROR;
			if (Sys_bRegister)
			{
				sip_new_nameuri(pBuf, pNumber);
			}
			else
			{
				// change '*' to '.'
				sip_star2dot(pNumber);
				strcpy(pBuf, _cTokenSip);
				strcat(pBuf, pNumber);
			}
			sip_send_refer(pBuf);
		}
		else
		{
			TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_XFER_FAILED);
		}
	}
}

void SipTransfer()
{
	SIP_LCB_HANDLE pAltLcb;
	UCHAR pBuf[256];
	PCHAR pTempBuf;
	PCHAR pTemp;
	USHORT s;

	if (Sip_pActiveLcb->iState != CALL_STATE_TALKING)	return;

	pAltLcb = sip_ln_alt();

	if (pAltLcb->iState == CALL_STATE_TALKING)
	{
/*		TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_CALL_XFER);
		line_start(pBuf);
//		line_add_str(pAltLcb->pReqUri);
		if (Sys_bRegister)
		{
			sip_new_nameuri(pBuf, pAltLcb->pPeerNumber);
			line_update_len();
		}
		else
		{
			line_add_str(_cTokenSip);
			line_add_str(pAltLcb->pPeerNumber);
		}
		line_add_str("?Replaces=");
		pTemp = line_get_buf();
		pTempBuf = Adapter_pUdpBuf;
		strcpy(pTempBuf, pAltLcb->pCallId);
		strcat(pTempBuf, ";to-tag=");
		strcat(pTempBuf, pAltLcb->pToTag);
		strcat(pTempBuf, ";from-tag=");
		strcat(pTempBuf, pAltLcb->pFromTag);
		sip_escape_str(pTemp, pTempBuf);

		Sip_pCurLcb = Sip_pActiveLcb;
		Sip_pCurLcb->iXferPart = XFER_PART_TRANSFEROR;
		pAltLcb->iXferPart = XFER_PART_TRANSFEROR;
		for (s = 0; s < 1000; s ++)
		{
			delay1ms();
		}
		sip_send_refer(pBuf);
*/
		TaskUIHandler(UI_CALL_DISPLAY_IDS0, IDS_CALL_XFER);
		line_start(pBuf);
		if (Sys_bRegister)
		{
			sip_new_nameuri(pBuf, Sip_pActiveLcb->pPeerNumber);
			line_update_len();
		}
		else
		{
			line_add_str(_cTokenSip);
			line_add_str(Sip_pActiveLcb->pPeerNumber);
		}
		line_add_str("?Replaces=");
		pTemp = line_get_buf();

		pTempBuf = Adapter_pUdpBuf;

		strcpy(pTempBuf, Sip_pActiveLcb->pCallId);
		strcat(pTempBuf, ";to-tag=");
		strcat(pTempBuf, Sip_pActiveLcb->pToTag);
		strcat(pTempBuf, ";from-tag=");
		strcat(pTempBuf, Sip_pActiveLcb->pFromTag);
		sip_escape_str(pTemp, pTempBuf);
		
		Sip_pCurLcb = pAltLcb;
		Sip_pCurLcb->iXferPart = XFER_PART_TRANSFEROR;
		Sip_pActiveLcb->iXferPart = XFER_PART_TRANSFEROR;
		for (s = 0; s < 1000; s ++)
		{
			delay1ms();
		}
		sip_send_refer(pBuf);
	}
	else if (pAltLcb->iState == CALL_STATE_FREE)
	{
		// hold current call
		if (Sip_pActiveLcb->iLocalMode & AUDIO_MODE_RECVONLY)
		{
			Sip_pCurLcb = Sip_pActiveLcb;
			sip_hold_call();
		}
		Sip_pActiveLcb = pAltLcb;
		Sip_pCurLcb = Sip_pActiveLcb;
		Sip_pCurLcb->iXferPart = XFER_PART_TRANSFEROR;
		SipDial();
	}
}
#endif

void SipCall(PCHAR pCalledUri)
{
	UCHAR pBuf[128];

	strcpy(pBuf, pCalledUri);
	Sip_pCurLcb = Sip_pActiveLcb;

#ifdef OEM_IP20
	TaskUIHandler(UI_CALL_CALLING, (USHORT)pCalledUri);
	SipCallStep1(pBuf);	
#ifdef __ADSL
	Sip_pCurLcb->brtpSession = FALSE;
	Sip_pCurLcb->brtpIpPrivate = TRUE;
#endif

#else
	if (Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEROR)
	{
		SipBlindTransfer(pBuf);
	}
	else
	{
		TaskUIHandler(UI_CALL_CALLING, (USHORT)pCalledUri);
		SipCallStep1(pBuf);
	}
#endif
}


void SipOutgoingKey(UCHAR iKey)
{
	UCHAR iType, iDtmfCode;
	
	iDtmfCode = ascii2keyid(iKey);
	if (iDtmfCode == MAP_KEY_UNKNOWN)	
	{
		return;
	}
	iType = OptionsGetByte(OPT_DTMF_TYPE);
	if (iType == DTMF_TYPE_RFC2833)
	{
		if (!(Sip_pActiveLcb->iLocalMode & AUDIO_MODE_SENDONLY) || !(Sip_pActiveLcb->iRemoteMode & AUDIO_MODE_RECVONLY))
		{
			return;
		}
		RtpSendKey(Sip_pActiveLcb, iDtmfCode);
	}
	else if (iType == DTMF_TYPE_SIPINFO)
	{
		Sip_pCurLcb = Sip_pActiveLcb;
		sip_send_info(iKey);
	}
}

void SipSendReq(UCHAR iMethod)
{
	switch (iMethod)
	{
	case SIP_REQ_REGISTER:
		sip_send_register();
		break;

	case SIP_REQ_SUBSCRIBE:
		sip_send_subscribe();
		break;

	case SIP_REQ_INVITE:
		sip_send_invite();
		break;

	case SIP_REQ_ACK:
		sip_send_ack();
		break;

	case SIP_REQ_BYE:
		sip_send_bye();
		break;
	
	case SIP_REQ_CANCEL:
		sip_send_cancel();
		break;

	case SIP_REQ_PRACK:
		sip_send_prack();
		break;
	}
}

void SipSendResp(USHORT sResp)
{
	UCHAR i;
	BOOLEAN bAddSdp;
	SIP_LCB_HANDLE pLcb;

	pLcb = NULL;
	bAddSdp = FALSE;
	if (Sip_iRecvMethod == SIP_REQ_OPTIONS)
	{
		bAddSdp = TRUE;
	}
	else if ((Sip_iRecvMethod == SIP_REQ_INVITE && (sResp == 200 || sResp == 183)) || (Sip_iRecvMethod == SIP_REQ_UPDATE && Sip_pRecvSdp && sResp == 200))
	{
		pLcb = Sip_pCurLcb;
		bAddSdp = TRUE;
	}
	if (Sip_iRecvMethod == SIP_REQ_INVITE && sResp == 183)
	{
		//183 is 180 with sdp
		sResp = 180;
	}

	sip_debug(_cSend, sResp);

	line_start(Sip_pTempBuf);
	for (i = 0; i < Sip_iRecvViaNum; i ++)
	{
		sip_add_header(_cVia, Sip_pRecvVia[i]);
	}
	for (i = 0; i < Sip_iRecvRRNum; i ++)
	{
		sip_add_header(_cRecord_Route, Sip_pRecvRecordRoute[i]);
	}
	sip_add_header(_cCSeq, Sip_pRecvCSeq);
	sip_add_header(_cCall_ID, Sip_pRecvCallId);
	sip_add_header(_cFrom, Sip_pRecvFrom);

	line_start(Adapter_pUdpBuf);
	sip_add_status_line(sResp);
	line_add_str(Sip_pTempBuf);
	if (Sip_pRecvSessionExpires && sResp == 200)
	{
		sip_add_session_expires(Sip_pCurLcb);
		sip_add_supported();
	}
	sip_add_header(_cTo, Sip_pRecvTo);
	if (Sip_iRecvMethod == SIP_REQ_INVITE)
	{
		if (sResp < 200 && Sip_pCurTcb->b100rel)
		{
			sip_add_require(_c100rel);
			sip_add_rseq(Sip_pCurLcb->lRseq);
		}
	}
	if (Sip_iRecvMethod == SIP_REQ_OPTIONS)
	{
		sip_add_allow();
		sip_add_supported();
	}
	if (sResp == 415 || Sip_iRecvMethod == SIP_REQ_OPTIONS)
	{
		sip_add_accept();
	}
	if (Sip_iRecvMethod == SIP_REQ_UNKNOWN)
	{
		sip_add_allow();
	}
	if (Sip_iRecvMethod == SIP_REQ_INVITE && sResp == 302)
	{
		sip_add_fwdcontact();
	}
	else
	{
		sip_add_contact();
	}
	sip_add_user_agent();
	// add content type and length
	if (bAddSdp)
	{
		sip_add_content_sdp(pLcb);
	}
	else
	{
		sip_add_content_length(0);
		line_add_crlf();
	}
	// send reponse
	sip_tr_send_resp(UdpGetDstIP(Sip_pSocket), Sip_sRespDstPort, sResp);
}

void SipDnd()
{
	Sip_pCurLcb = Sip_pActiveLcb;
	sip_send_invresp(480);
	Sip_pCurLcb->iState = CALL_STATE_HANGUP;
	TaskCallLogAdd(Sip_pCurLcb->pPeerName, Sip_pCurLcb->pPeerNumber, CALL_LOG_TYPE_RECVED);
	sip_check_altline();
}


BOOLEAN SipXfereeDone(USHORT sResp)
{
	SIP_LCB_HANDLE pAltLcb;

	Sip_pCurLcb->iXferPart = XFER_PART_NONE;
	if (Sip_pCurLcb->pReplaces)		
	{
		free(Sip_pCurLcb->pReplaces);
		Sip_pCurLcb->pReplaces = NULL;
	}
	if (Sip_pCurLcb->pReferredBy)	
	{
		free(Sip_pCurLcb->pReferredBy);
		Sip_pCurLcb->pReferredBy = NULL;
	}

	pAltLcb = Sip_pCurLcb;
	Sip_pCurLcb = sip_ln_alt();
	if (Sip_pCurLcb->iXferPart == XFER_PART_TRANSFEREE_ORIG)
	{
		Sip_pCurLcb->iXferPart = XFER_PART_NONE;
#ifdef OEM_IP20
		if (Sip_pCurLcb->iState == CALL_STATE_TALKING || Sip_pCurLcb->iState == CALL_STATE_DIALING)
#else
		if (Sip_pCurLcb->iState == CALL_STATE_TALKING)
#endif
		{
			sip_send_notify(sResp);
			if (sResp >= 300)
			{
				Sip_pActiveLcb = Sip_pCurLcb;
				if (sResp != USHORT_MAX)
				{
					Sip_pCurLcb = pAltLcb;
					sip_ln_free();
					Sip_pCurLcb = Sip_pActiveLcb;
				}
			}
			return TRUE;
		}
	}

	return FALSE;
}

void SipHandleEvent(UCHAR iEvent, USHORT sParam)
{
	PCHAR p;
	UCHAR iState;

	iState = Sip_pActiveLcb->iState;
	switch (iEvent)
	{
	case UI_EVENT_LOGON:
		SipStartup();
		SipLogon();
		break;

	case UI_EVENT_HANDSET_UP:
		SipHandsetUp((BOOLEAN)sParam);
		break;

	case UI_EVENT_HANDSET_DOWN:
		SipHandsetDown();
		break;

	case UI_EVENT_FLASH:
		SipFlash();
		break;

	case UI_EVENT_HOLD:
		if (sParam)
		{
			SipUnhold();
		}
		else
		{
			SipHold();
		}
		break;

	case UI_EVENT_CALL:
		if (iState == CALL_STATE_DIALING)
		{
			Sip_pCurLcb = Sip_pActiveLcb;
			SipCall((PCHAR)sParam);
		}
		break;

	case UI_EVENT_MEM_CALL:
		if (iState == CALL_STATE_DIALING)
		{
			p = strchr((PCHAR)sParam, 'T');
			if (p)
			{
				strcpy(Sip_pActiveLcb->pKeyOut, p);
				Sip_pActiveLcb->iKeyIndex = 0;
				*p = 0;
			}
			if (!Sip_pActiveLcb->bMemCall)
			{
				TaskUIHandler(UI_CALL_PLAYKEYS, sParam);
				SipCall((PCHAR)sParam);
			}
			else
			{
				strcpy(Sip_pActiveLcb->pPeerNumber, (PCHAR)sParam);
			}
		}
		else if (iState == CALL_STATE_TALKING)
		{
			strcpy(Sip_pActiveLcb->pKeyOut, (PCHAR)sParam);
			Sip_pActiveLcb->iKeyIndex = 0;
		}
		break;

	case UI_EVENT_TRANSFER:
#ifdef OEM_IP20
		SipTransfer((PCHAR)sParam);
#else
		SipTransfer();
#endif
		break;

	case UI_EVENT_DIGIT:
		TaskUIHandler(UI_CALL_PLAYKEY, (UCHAR)sParam);
		SipOutgoingKey((UCHAR)sParam);
		break;

	case UI_EVENT_MUTE_DND:
		if (Sip_pActiveLcb->iState == CALL_STATE_RINGING)
		{
			SipDnd();
		}
		break;

	// Event from SIPR bank
	case SIPR_EVENT_LOGON_OK:
		sip_logon_ok();
		break;

	case SIPR_EVENT_LOGON_FAILED:
		sip_logon_failed();
		break;

	case SIPR_EVENT_SUB_FAILED:
		sip_sub_failed();
		break;

	case SIPR_EVENT_SEND_REQ:
		SipSendReq((UCHAR)sParam);
		break;

	case SIPR_EVENT_SEND_RESP:
		SipSendResp(sParam);
		break;

	case SIPR_EVENT_CALL_CANCELED:
		SipCallCanceled();
		break;

	case SIPR_EVENT_CALL_FORWARDED:
		SipCallStep3();
		break;

	case SIPR_EVENT_AUTO_ANSWER:
		SipAnswer();
		break;

	case SIPR_EVENT_LINE_FREE:
		sip_ln_free();
		break;
	
	case SIPR_EVENT_XFEREE_DONE:
		SipXfereeDone(sParam);
		break;

	case SIPR_EVENT_NEW_TAG:
		sip_new_token((PCHAR)sParam, MAX_TAG_LEN);
		break;

	case SIPR_EVENT_MODIFY_RTPPORT:
		sip_modify_rtpport();
		break;
	}
}

USHORT SipGetCallInfo(UCHAR iType)
{
	switch (iType)
	{
	case CALL_INFO_STATE:
		return Sip_pActiveLcb->iState;

	case CALL_INFO_IS_CALLEE:
		return Sip_pActiveLcb->bCallee;

	case CALL_INFO_PEER_NAME:
		return (USHORT)(Sip_pActiveLcb->pPeerName);

	case CALL_INFO_PEER_NUMBER:
		return (USHORT)(Sip_pActiveLcb->pPeerNumber);

	case CALL_INFO_CALL_TIME:
		return (USHORT)(&Sip_pActiveLcb->ct);
	}
	return 0;
}

void SipFrameTimer()
{
	UCHAR i, iCoder;
	USHORT sTimeStamp;

	if (!Sip_pActiveLcb->bSendingKey)	return;

	if (Sip_pActiveLcb->bHolding || Sip_pActiveLcb->iXferPart == XFER_PART_TRANSFEROR)
	{
		return;
	}
	iCoder = Sip_pActiveLcb->iVoiceCoder;
	if (iCoder == VOICE_CODER_NONE || iCoder > VOICE_CODER_NUM)
	{
		return;
	}

	/* Handle DTMF Keys */
	// Net2phone MTA adapter's RFC2833 handling
	if (Sip_pActiveLcb->sSendKeyLen < RTP_DTMF_DURATION)
	{
		sTimeStamp = GetFrameTime(iCoder) << 3;
		if (Sip_pActiveLcb->sSendKeyLen)	
		{
			RtpSendKeyPacket(Sip_pActiveLcb, FALSE, FALSE);
			Sip_pActiveLcb->sSendKeyLen += sTimeStamp;
		}
		else
		{
			Sip_pActiveLcb->lRtpTimeStamp = rl_32x8((Sys_lTicker - Sip_pActiveLcb->lRtpOffset), 3);
			for (i = 0; i < 3; i ++)
			{
				RtpSendKeyPacket(Sip_pActiveLcb, TRUE, FALSE);
			}
			Sip_pActiveLcb->sSendKeyLen += sTimeStamp;
			RtpSendKeyPacket(Sip_pActiveLcb, FALSE, FALSE);
			Sip_pActiveLcb->sSendKeyLen += sTimeStamp;
		}
	}
	else 
	{
		for (i = 0; i < 3; i ++)
		{
			RtpSendKeyPacket(Sip_pActiveLcb, FALSE, TRUE);
		}
		Sip_pActiveLcb->bSendingKey = FALSE;
	}
}

void SipSendVoice(UCHAR iLen)
{
	UCHAR iState;

	iState = Sip_pActiveLcb->iState;
#if defined OEM_IP20 || defined OEM_BT2008
	if (iState == CALL_STATE_RINGING)
	{
		return;	//added in order to not make RTP connection during ringing
	}
#else
	if (iState != CALL_STATE_CALLING2 && iState != CALL_STATE_TALKING && iState != CALL_STATE_RINGING)
	{
		return;
	}
#endif
	if (Sip_pActiveLcb->bHolding || Sip_pActiveLcb->iXferPart == XFER_PART_TRANSFEROR)
	{
		return;
	}
/*	// If not sending packets continuously, ringback tone packets may not be able to reach caller.
	if (iState == CALL_STATE_CALLING2)
	{
		if (Sip_pActiveLcb->iPacketCtr >= 6)
		{
			Sip_pActiveLcb->bVoiceStart = TRUE;
			return;
		}
	}
*/
	if (!(Sip_pActiveLcb->iLocalMode & AUDIO_MODE_SENDONLY) || !(Sip_pActiveLcb->iRemoteMode & AUDIO_MODE_RECVONLY))
	{
		return;
	}
	if (Sip_pActiveLcb->iVoiceCoder == VOICE_CODER_NONE || Sip_pActiveLcb->iVoiceCoder > VOICE_CODER_NUM)
	{
		return;
	}
	if (Sip_pActiveLcb->bSendingKey)
	{
		return;
	}

	RtpSendVoice(Sip_pActiveLcb, iLen);
}

void SipTimer()
{
	UCHAR i;
	SIP_LCB_HANDLE pLcb;
	SIP_LCB_HANDLE pAltLcb;
	UCHAR iState, iKey, iNoAnswerTO;

	sip_tr_timer();
	
	if (Sys_bRegister)
	{
		sip_logon_timer();
		sip_sub_timer();
	}

	pLcb = Sip_pLCB;
	iNoAnswerTO = OptionsGetByte(OPT_NOANSWER_TIMEOUT);
	for (i = 0; i < LCB_TSIZE; i ++, pLcb ++)
	{
		iState = pLcb->iState;
		Sip_pCurLcb = pLcb;
		if (iState == CALL_STATE_RINGING)
		{
			pLcb->iTimer ++;
#ifdef OEM_IP20
			if (TransferFlag == 1 && pLcb->iTimer > 2)
			{
				Sip_pCurLcb = pLcb;
				sip_send_invresp(480);
				pLcb->iState = CALL_STATE_HANGUP;
			
				//TaskCallLogAdd(Sip_pCurLcb->pPeerName, Sip_pCurLcb->pPeerNumber, CALL_LOG_TYPE_MISSED);
				if (pLcb == Sip_pActiveLcb)
				{
					sip_check_altline();
				}
				TransferFlag = 0 ;
			}
#endif
			if (iNoAnswerTO && (pLcb->iTimer > iNoAnswerTO))
			{
				if (OptionsGetByte(OPT_AUTO_ANSWER))
				{
					if (pLcb == Sip_pActiveLcb)
					{
						TaskUIHandler(UI_CALL_AUTOANSWER, 0); 
						SipAnswer();
					}
				}
				else
				{
					if (OptionsGetByte(OPT_FWD_NOANSWER))
					{
						sip_send_invresp(302);
					}
					else
					{
						sip_send_invresp(480);
					}
					pLcb->iState = CALL_STATE_HANGUP;
					TaskCallLogAdd(Sip_pCurLcb->pPeerName, Sip_pCurLcb->pPeerNumber, CALL_LOG_TYPE_MISSED);
					//sip_ln_free();
					if (pLcb == Sip_pActiveLcb)
					{
						sip_check_altline();
					}
				}
			}
		}
		else if (iState == CALL_STATE_TALKING)
		{
			CallTimeTick(&pLcb->ct);
			if (pLcb == Sip_pActiveLcb)
			{
				pAltLcb = sip_ln_alt();
#ifdef OEM_IP20
				if (pLcb->iLocalMode == AUDIO_MODE_SENDRECV)
#else
				if (pLcb->iLocalMode == AUDIO_MODE_SENDRECV && pLcb->iXferPart == XFER_PART_NONE)
#endif
				{
					if (pAltLcb->iState != CALL_STATE_RINGING)
					{
						TaskUIHandler(UI_CALL_TIMER, (USHORT)&pLcb->ct);
					}
					else
					{
						if (mod_16x8(pAltLcb->iTimer, 10) == 9)
						{
							TaskUIHandler(UI_CALL_WAITING, 0);
						}
					}
				}
				iKey = pLcb->pKeyOut[pLcb->iKeyIndex];
				if (iKey)
				{
					if (iKey == 'T')
					{
						pLcb->iTimer ++;
						if (pLcb->iTimer >= 4)
						{
							pLcb->iTimer = 0;
							pLcb->iKeyIndex ++;
						}
					}
					else
					{
						TaskUIHandler(UI_CALL_PLAYKEY, (USHORT)iKey);
						SipOutgoingKey(iKey);
						pLcb->iKeyIndex ++;
						pLcb->iTimer = 0;
					}
				}
			}
			if (pLcb->iXferPart == XFER_PART_FAIL)
			{
				pLcb->iTimer ++;
				if (pLcb->iTimer >= 2)
				{
					pLcb->iTimer = 0;
					pLcb->iXferPart = XFER_PART_NONE;
				}
			}
			if (pLcb->sSessionExpires)
			{
				pLcb->sSessionTimer ++;
				if (pLcb->sSessionTimer == pLcb->sSessionExpires)
				{
					sip_send_bye();
					Sip_pCurLcb->iState = CALL_STATE_HANGUP;
				}
				else if (pLcb->sSessionTimer == (pLcb->sSessionExpires >> 1))
				{
					if ((pLcb->bCallee && pLcb->iRefresher == SIP_REFRESHER_UAS) || (!pLcb->bCallee && pLcb->iRefresher == SIP_REFRESHER_UAC))
					{
						sip_send_reinvite();
					}
				}
			}
		}
		else if (iState == CALL_STATE_DIALING)
		{
			if (pLcb == Sip_pActiveLcb)
			{
				if (pLcb->bMemCall)
				{
					pLcb->iTimer ++;
					if (pLcb->iTimer >= 2)
					{
						pLcb->iTimer = 0;
						pLcb->bMemCall = FALSE;
						TaskUIHandler(UI_CALL_PLAYKEYS, (USHORT)pLcb->pPeerNumber);
						SipCall(pLcb->pPeerNumber);
					}
				}
			}
		}
		else if (iState == CALL_STATE_HANGUP)
		{
			pLcb->iTimer ++;
			if (pLcb->iTimer >= HANGUP_TIMEOUT)
			{
				sip_ln_free();
				if (pLcb == Sip_pActiveLcb)
				{
					TaskUIHandler(UI_CALL_HANGUP, 0);
				}
			}
		}
	}
}

void SipStunDone(PCHAR pMappedAddr, USHORT sMappedPort, UCHAR iTask)
{
	Sip_pCurLcb = Sip_pActiveLcb;
	if (pMappedAddr)
	{
		memcpy4(Sip_pNatIP, pMappedAddr);
	}
	else
	{
		memcpy4(Sip_pNatIP, Sys_pIpAddress);
	}

	switch(iTask)
	{
	case STUN_TASK_LOGON:
		if (sMappedPort)
		{
			Sip_sNatPort = sMappedPort;
		}
		SipLogonStep1();
		break;

	case STUN_TASK_CALL:
		if (Sip_pCurLcb->iState == CALL_STATE_CALLING2)
		{
			if (sMappedPort)
			{
				Sip_pCurLcb->sRtpPort = sMappedPort;
			}
			sip_send_invite();
		}
		break;

	case STUN_TASK_ANSWER:
		if (Sip_pCurLcb->iState == CALL_STATE_TALKING)
		{
			if (sMappedPort)
			{
				Sip_pCurLcb->sRtpPort = sMappedPort;
			}
			sip_send_invresp(200);
		}
		break;
	}
}

#endif
