#include "version.h"
#include "PalmType.h"
#include "PalmChip.h"
#include "p_util.h"
#include "p_tcpip.h"
#include "p_http.h"
#include "v_task.h"

#if defined CALL_SIP

#define ALGORITHM_MD5		0
#define ALGORITHM_MD5SESS	1

#define QOP_AUTH		0
#define QOP_AUTHINT		1
#define QOP_NONE		2

#define STALE_LABEL_LEN		6
#define ALGORITHM_LABEL_LEN		10
#define MD5SESS_LABEL_LEN	8
#define MD5_LABEL_LEN		3

static CCHAR _cBasic[] = "Basic";
static CCHAR _cDigest[] = "Digest";

#define REALM_LABEL_LEN		6
static CCHAR _cRealm[] = "realm=";
#define NONCE_LABEL_LEN		6
static CCHAR _cNonce[] = "nonce=";
#define OPAQUE_LABEL_LEN	7
static CCHAR _cOpaque[] = "opaque=";
#define QOP_LABEL_LEN		4
static CCHAR _cQop[] = "qop=";

static CCHAR _cStale[] = "stale=";
static CCHAR _cAlgorithm[] = "algorithm=";
static CCHAR _cMD5Sess[] = "MD5-sess";
static CCHAR _cMD5[] = "MD5";

static CCHAR _cQopType[][9] = {"auth", "auth-int"};

static CCHAR _cCnonce[] = "0a4f113b";
static CCHAR _cNc[] = "00000001";
static CCHAR _cHEntity[] = "d41d8cd98f00b204e9800998ecf8427e";

static CCHAR _cAuthorization[] = "Authorization";
static CCHAR _cProxyAuthorization[] = "Proxy-Authorization";

static CCHAR _cBase64Table[] = {
	'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', '0', '1', '2', '3', 
	'4', '5', '6', '7', '8', '9', '+', '/'
};


/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

 UCHAR HttpReadData(UCHAR iIndexLow)
{
	SdramRead(HTTP_SDRAM_HI, iIndexLow, SRAM_DMA_HALF_DWORD);
	return X_VAL(Sdram_pDataCache, SDRAM_DATA_LENGTH);
}

UCHAR HttpCopyData(UCHAR iSrcIndexLow, PXCHAR pDst)
{
	DCHAR iLen;
	iLen = HttpReadData(iSrcIndexLow);
	if (iLen)
	{
		p_memcpy_x2x(pDst, (PXCHAR)Sdram_pDataCache, iLen);
	}
	return iLen;
}

 void HttpWriteData(UCHAR iIndexLow, UCHAR iLen)
{
	X_VAL(Sdram_pDataCache, SDRAM_DATA_LENGTH) = iLen;
	SdramWrite(HTTP_SDRAM_HI, iIndexLow, SRAM_DMA_HALF_DWORD);
}

 void HttpReadAll(UCHAR iIndexLow)
{
	SdramRead(HTTP_SDRAM_HI, iIndexLow, SRAM_DMA_DWORD);
}

 void HttpWriteAll(UCHAR iIndexLow)
{
	SdramWrite(HTTP_SDRAM_HI, iIndexLow, SRAM_DMA_DWORD);
}

UCHAR HttpReadString(UCHAR iSdramLo)
{
	DCHAR iLen;

	iLen = HttpReadData(iSdramLo);
	X_VAL(Sdram_pDataCache, iLen) = 0;
	return iLen;
}

/*
UCHAR HttpWriteString(UCHAR iSdramLo)
{
	DCHAR iLen;

	iLen = p_strlen_x((PXCHAR)Sdram_pDataCache);
	HttpWriteData(iSdramLo, iLen);
	return iLen;
}
*/

void HttpSaveData(UCHAR iDstIndexLow, PXCHAR pSrc, UCHAR iLen)
{
	if (iLen)
	{
		p_memcpy_x2x_0((PXCHAR)Sdram_pDataCache, pSrc, iLen);
	}
	HttpWriteData(iDstIndexLow, iLen);
}


 void HttpSaveString(UCHAR iIndexLow, PXCHAR pStr)
{
	DCHAR iLen;

	iLen = p_strlen_x(pStr);
	HttpSaveData(iIndexLow, pStr, iLen);
}

 void HttpAddString(UCHAR iSdramLo)
{
	HttpReadString(iSdramLo);
	p_line_add_x((PXCHAR)Sdram_pDataCache);
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void _FillAuthHeader(UCHAR iType)
{
	if (iType == HTTP_AUTH_TYPE_PROXY)
	{
		p_line_add_c(_cProxyAuthorization);
	}
	else //if (iType = HTTP_AUTH_TYPE_WWW)
	{
		p_line_add_c(_cAuthorization);
	}
	p_line_add_colon();
}

PXCHAR _UnquoteStr(PXCHAR pStr)
{
	PXCHAR_D pCur;
	PXCHAR_D pTemp;

	pCur = pStr;
	pTemp = p_strchr_x(pCur, '"');
	if (pTemp)
	{
		pCur = pTemp;
		pCur ++;
		pTemp = p_strchr_x(pCur, '"');
		if (pTemp)	pTemp[0] = 0;
	}
	return pCur;
}

void _QuoteStr(PXCHAR pSrc)
{
	p_line_add_char('"');
	p_line_add_x(pSrc);
	p_line_add_char('"');
}

/*****************************************************************
*** Basic Authentication *****************************************
******************************************************************/
void _GetBasicCredit(PXCHAR pDst)
{
	XCHAR pBuffer[MAX_ACCOUNT_LEN + MAX_PIN_LEN + 4];
	DCHAR i, j, iLen, iIndex;

	p_memset_x(pBuffer, 0, MAX_ACCOUNT_LEN + MAX_PIN_LEN + 4);
	iLen = HttpCopyData(HTTP_AUTH_ACCOUNT, pBuffer);
	pBuffer[iLen] = ':';
	iLen ++;
	iLen += HttpCopyData(HTTP_AUTH_PASSWORD, (PXCHAR)(pBuffer + iLen));

	for (i = 0, j = 0; i < (UCHAR)(iLen - 2); i += 3)
	{
		iIndex = (pBuffer[i] & 0xfc) >> 2;
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		iIndex = ((pBuffer[i] & 0x03) << 4) + ((pBuffer[i+1] & 0xf0) >> 4);
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		iIndex = ((pBuffer[i+1] & 0x0f) << 2) + ((pBuffer[i+2] & 0xc0) >> 6);
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		iIndex = pBuffer[i+2] & 0x3f;
		pDst[j] = _cBase64Table[iIndex];
		j ++;
	}

	if (i == (UCHAR)(iLen - 1))
	{
		iIndex = (pBuffer[i] & 0xfc) >> 2;
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		iIndex = (pBuffer[i] & 0x03) << 4;
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		pDst[j] = '=';
		j ++;
		pDst[j] = '=';
		j ++;
	}
	else if (i == (UCHAR)(iLen - 2))
	{
		iIndex = (pBuffer[i] & 0xfc) >> 2;
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		iIndex = ((pBuffer[i] & 0x03) << 4) + ((pBuffer[i+1] & 0xf0) >> 4);
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		iIndex = (pBuffer[i+1] & 0x0f) << 2;
		pDst[j] = _cBase64Table[iIndex];
		j ++;
		pDst[j] = '=';
		j ++;
	}

	pDst[j] = 0;
}

/*****************************************************************
*** Digest Authentication ****************************************
******************************************************************/
void _GenHA1(UCHAR iAlgorithm)
{
	p_line_backup();
	p_line_start((PXCHAR)Sdram_pDataCache2);
	HttpAddString(HTTP_AUTH_ACCOUNT);
	p_line_add_char(':');
	HttpAddString(HTTP_AUTH_REALM);
	p_line_add_char(':');
	HttpAddString(HTTP_AUTH_PASSWORD);
	p_line_restore();
	MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, p_strlen_x((PXCHAR)Sdram_pDataCache2));
	if (iAlgorithm == ALGORITHM_MD5SESS)
	{
		p_memcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, MD5_SIGNATURE_SIZE);
		p_line_backup();
		p_line_start((PXCHAR)(Sdram_pDataCache2 + MD5_SIGNATURE_SIZE));
		p_line_add_char(':');
		HttpAddString(HTTP_AUTH_CALC_NONCE);
		p_line_add_char(':');
		p_line_add_c(_cCnonce);
		p_line_restore();
		MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, p_strlen_x((PXCHAR)Sdram_pDataCache2));
	}
	p_char2asc_str((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, MD5_SIGNATURE_SIZE, FALSE);
	HttpSaveString(HTTP_AUTH_HA1, (PXCHAR)Sdram_pDataCache2);
}

void _GenHA2(UCHAR iQopType, PCCHAR pMethod)
{
	p_line_backup();
	p_line_start((PXCHAR)Sdram_pDataCache2);
	p_line_add_c(pMethod);
	p_line_add_char(':');
	HttpAddString(HTTP_AUTH_URI);
	if (iQopType == QOP_AUTHINT)
	{
		p_line_add_char(':');
		p_line_add_c(_cHEntity);
	}
	p_line_restore();
	MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, p_strlen_x((PXCHAR)Sdram_pDataCache2));
	p_char2asc_str((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, MD5_SIGNATURE_SIZE, FALSE);
	HttpSaveString(HTTP_AUTH_HA2, (PXCHAR)Sdram_pDataCache2);
}

void _GenDigest(UCHAR iQopType)
{
	p_line_backup();
	p_line_start((PXCHAR)Sdram_pDataCache2);
	HttpAddString(HTTP_AUTH_HA1);
	p_line_add_char(':');
	HttpAddString(HTTP_AUTH_CALC_NONCE);
	p_line_add_char(':');
	if (iQopType != QOP_NONE)
	{
		p_line_add_c(_cNc);
		p_line_add_char(':');
		p_line_add_c(_cCnonce);
		p_line_add_char(':');
		p_line_add_c(_cQopType[iQopType]);
		p_line_add_char(':');
	}
	HttpAddString(HTTP_AUTH_HA2);
	p_line_restore();
	MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, p_strlen_x((PXCHAR)Sdram_pDataCache2));
	p_char2asc_str((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, MD5_SIGNATURE_SIZE, FALSE);
	HttpSaveString(HTTP_AUTH_DIGEST, (PXCHAR)Sdram_pDataCache2);
}

 void HttpAuthInit()
{
	HttpWriteData(HTTP_AUTH_REALM, 0);
	HttpWriteData(HTTP_AUTH_NONCE, 0);
	HttpWriteData(HTTP_AUTH_CALC_NONCE, 0);
	HttpWriteData(HTTP_AUTH_OPAQUE, 0);
	HttpWriteData(HTTP_AUTH_DIGEST, 0);
	HttpWriteData(HTTP_AUTH_HA1, 0);
	HttpWriteData(HTTP_AUTH_HA2, 0);
	HttpWriteData(HTTP_AUTH_URI, 0);
	HttpWriteData(HTTP_AUTH_ACCOUNT, 0);
	HttpWriteData(HTTP_AUTH_PASSWORD, 0);
}

 void HttpClearAuth()
{
	p_memset_x((PXCHAR)Sdram_pDataCache, 0, 2);
	HttpWriteAll(HTTP_WWW_AUTH_RESP);
	p_memset_x((PXCHAR)Sdram_pDataCache, 0, 2);
	HttpWriteAll(HTTP_PROXY_AUTH_RESP);
}

 void HttpGenAuth(PXCHAR pAuthData, PCCHAR pMethod, UCHAR iType)
{
	PXCHAR_D pCur;
	PXCHAR_D pTemp;
	DSHORT iLen;
	DCHAR iAlg, iQop;

	pCur = (PXCHAR)pAuthData;
	pTemp = p_strchr_x(pCur, ' ');
	if (!pTemp)		return;
	pTemp[0] = 0;

	p_line_start(Adapter_pSendPacket);

	if (p_strcmp_nocase_c2x(pCur, _cBasic))
	{
		_FillAuthHeader(iType);
		p_line_add_c(_cBasic);
		p_line_add_space();	
		_GetBasicCredit((PXCHAR)Sdram_pDataCache2);
		p_line_add_x((PXCHAR)Sdram_pDataCache2);
		p_line_add_newline();
		goto End;
	}

	if (!p_strcmp_nocase_c2x(pCur, _cDigest))
	{
		return;
	}

	iQop = QOP_NONE;
	iAlg = ALGORITHM_MD5;
	pCur = pTemp;
	pCur ++; // pass space
	do
	{
		pTemp = p_strchr_x(pCur, ',');
		if (pTemp)	pTemp[0] = 0;
		if (p_memcmp_nocase_c2x(pCur, _cRealm, REALM_LABEL_LEN))
		{
			pCur += REALM_LABEL_LEN;
			pCur = _UnquoteStr(pCur);
			HttpSaveString(HTTP_AUTH_REALM, pCur);
		}
		else if (p_memcmp_nocase_c2x(pCur, _cNonce, NONCE_LABEL_LEN))
		{
			pCur += NONCE_LABEL_LEN;
			pCur = _UnquoteStr(pCur);
			HttpSaveString(HTTP_AUTH_NONCE, pCur);
			if (Sys_iProtocol == PROTOCOL_SIP && (Sys_iServiceType == SIP_SERVICE_TYPE_MEDIARING || Sys_iServiceType == SIP_SERVICE_TYPE_RINGCHAT))
			{
				TaskUserData(SIP_MEDIARING_ENCRYPT, pCur);
			}
			HttpSaveString(HTTP_AUTH_CALC_NONCE, pCur);
		}
		else if (p_memcmp_nocase_c2x(pCur, _cOpaque, OPAQUE_LABEL_LEN))
		{
			HttpSaveString(HTTP_AUTH_OPAQUE, pCur);
		}
		else if (p_memcmp_nocase_c2x(pCur, _cQop, QOP_LABEL_LEN))
		{
			iQop = QOP_AUTH;
		}
		else if (p_memcmp_nocase_c2x(pCur, _cAlgorithm, ALGORITHM_LABEL_LEN))
		{
			pCur += ALGORITHM_LABEL_LEN;
			pCur = _UnquoteStr(pCur);
			if (p_memcmp_nocase_c2x(pCur, _cMD5Sess, MD5SESS_LABEL_LEN))
			{
				iAlg = ALGORITHM_MD5SESS;
			}
		}
		if (pTemp)
		{
			pCur = pTemp;
			pCur ++;
			while (*pCur == ' ' || *pCur == '\t')
			{
				pCur ++;
			}
		}
	}while (pTemp);		
	
	_GenHA1(iAlg);
	_GenHA2(iQop, pMethod);
	_GenDigest(iQop);

	_FillAuthHeader(iType);
	p_line_add_c(_cDigest);
	p_line_add_c((PCCHAR)" username=");
	HttpReadString(HTTP_AUTH_ACCOUNT);
	_QuoteStr((PXCHAR)Sdram_pDataCache);
	p_line_add_comma();

	p_line_add_c(_cRealm);
	HttpReadString(HTTP_AUTH_REALM);
	_QuoteStr((PXCHAR)Sdram_pDataCache);
	p_line_add_comma();

	p_line_add_c(_cNonce);
	HttpReadString(HTTP_AUTH_NONCE);
	_QuoteStr((PXCHAR)Sdram_pDataCache);
	p_line_add_comma();

	p_line_add_c((PCCHAR)"uri=");
	HttpReadString(HTTP_AUTH_URI);
	_QuoteStr((PXCHAR)Sdram_pDataCache);
	p_line_add_comma();

	if (iQop != QOP_NONE)
	{
		p_line_add_c(_cQop);
		p_line_add_c(_cQopType[iQop]);
		p_line_add_comma();

		p_line_add_c((PCCHAR)"nc=");
		p_line_add_c(_cNc);
		p_line_add_comma();

		p_line_add_c((PCCHAR)"cnonce=\"");
		p_line_add_c(_cCnonce);
		p_line_add_char('"');
		p_line_add_comma();
	}

	p_line_add_c((PCCHAR)"response=");
	HttpReadString(HTTP_AUTH_DIGEST);
	_QuoteStr((PXCHAR)Sdram_pDataCache);
	if (HttpReadString(HTTP_AUTH_OPAQUE))
	{
		p_line_add_comma();
		p_line_add_x((PXCHAR)Sdram_pDataCache);
	}

	p_line_add_comma();
	p_line_add_c(_cAlgorithm);
	if (iAlg == ALGORITHM_MD5)
	{
		p_line_add_c(_cMD5);
	}
	else
	{
		p_line_add_c(_cMD5Sess);
	}
	if (Sys_iProtocol == PROTOCOL_SIP && Sys_iServiceType == SIP_SERVICE_TYPE_MEDIARING)
	{
		p_line_add_comma();
		p_line_add_c((PCCHAR)"mac=\"");
		p_Mac2String(Local_pMacAddress, p_line_get_buf());
		p_line_update_len();
		p_line_add_char('"');
	}
	p_line_add_newline();

End:
	iLen = p_line_get_len();
	USHORT2PXCHAR(iLen, (PXCHAR)Sdram_pDataCache);
	p_wmemcpy_x2x((PXCHAR)(Sdram_pDataCache+2), Adapter_pSendPacket, (USHORT)(iLen+1));
	if (iType == HTTP_AUTH_TYPE_PROXY)
	{
		HttpWriteAll(HTTP_PROXY_AUTH_RESP);
	}
	else //if (iType = HTTP_AUTH_TYPE_WWW)
	{
		HttpWriteAll(HTTP_WWW_AUTH_RESP);
	}
}

 void HttpFillAuth(UCHAR iRespIndex)
{
	DSHORT iLen;

	HttpReadAll(iRespIndex);
	iLen = PXCHAR2USHORT((PXCHAR)Sdram_pDataCache);
	if (iLen)
	{
		p_wmemcpy_x2x(p_line_get_buf(), (PXCHAR)(Sdram_pDataCache + 2), (USHORT)(iLen+1));
		p_line_inc_len(iLen);
	}
}

#endif
