/*-------------------------------------------------------------------------
   HTTP Digest Authentication function copy right information

   Copyright (C) 2006-2008. 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 "type.h"
#include "core.h"
#include "apps.h"
#include "bank6.h"

#define ALG_MD5		0
#define ALG_MD5SESS	1
#define ALG_NONE	2

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

const UCHAR _cCnonce[] = "0a4f113b";
const UCHAR _cNc[] = "00000001";
const UCHAR _cHEntity[] = "d41d8cd98f00b204e9800998ecf8427e";
const UCHAR _cQopType[][9] = {"auth", "auth-int"};
const UCHAR _cAlgType[][9] = {"MD5", "MD5-sess"};

PCHAR _unquote_str(PCHAR pStr)
{
	PCHAR pTemp;
	PCHAR pCur;

	pCur = pStr;
	pTemp = strchr(pCur, '"');
	if (pTemp)
	{
		pCur = pTemp;
		pCur ++;
		pTemp = strchr(pCur, '"');
		if (pTemp)	*pTemp = 0;
	}
	else
	{
		pTemp = strchr(pCur, ' ');
		if (pTemp)	*pTemp = 0;
	}
	return pCur;
}

void http_digest(PCHAR pDstBuf, PCHAR pAuthData, PCHAR pUri, PCHAR pAccount, PCHAR pPassword, PCHAR pMethod)
{
	PCHAR pCur;
	PCHAR pTemp;
	PCHAR pValue;
	PCHAR pRealm;
	PCHAR pNonce;
	PCHAR pOpaque;
	BOOLEAN bRealm, bNonce, bOpaque;
	UCHAR iAlg, iQop;
	UCHAR pHA1[33];
	UCHAR pHA2[33];
	UCHAR pMD5Value[MD5_SIGNATURE_SIZE];

	strcpy(pDstBuf, pAuthData);
	pCur = pDstBuf;
//	to_lower(pCur, 7);
	if (memcmp_lowercase(pCur, "digest "))
	{
		pDstBuf[0] = 0;
		return;
	}
	pCur += 7;

	iQop = QOP_NONE;
	iAlg = ALG_NONE;
	pRealm = NULL;
	pNonce = NULL;
	pOpaque = NULL;
	bRealm = FALSE;
	bNonce = FALSE;
	bOpaque = FALSE;
	do
	{
		pTemp = strchr(pCur, ',');
		if (pTemp)	*pTemp = 0;
		pValue = strchr(pCur, '=');
		if (pValue)
		{
			*pValue = 0;
			pValue ++;
		}

//		str_to_lower(pCur);
		pValue = _unquote_str(pValue);
		if (!strcmp_lowercase(pCur, "realm"))
		{
			if (!bRealm)
			{
				pRealm = heap_save_str(pValue);
				bRealm = TRUE;
			}
		}
		else if (!strcmp_lowercase(pCur, "nonce"))
		{
			if (!bNonce)
			{
				pNonce = heap_save_str(pValue);
				bNonce = TRUE;
			}
		}
		else if (!strcmp_lowercase(pCur, "opaque"))
		{
			if (!bOpaque)
			{
				pOpaque = heap_save_str(pValue);
				bOpaque = TRUE;
			}
		}
		else if (!strcmp_lowercase(pCur, "qop"))
		{
			iQop = QOP_AUTH;
		}
		else if (!strcmp_lowercase(pCur, "algorithm"))
		{
//			str_to_lower(pValue);
			if (!strcmp_lowercase(pValue, "md5"))
			{
				iAlg = ALG_MD5;
			}
			if (!strcmp_lowercase(pValue, "md5-sess"))
			{
				iAlg = ALG_MD5SESS;
			}
		}
		if (pTemp)
		{
			pCur = pTemp;
			pCur ++;
			pCur += count_space(pCur);
		}
	} while (pTemp);	

	line_backup();
	// generate HA1
	line_start(pDstBuf);
	line_add_str_with_char(pAccount, ':');
	line_add_str_with_char(pRealm, ':');
	line_add_str(pPassword);
	MD5GenValue(pHA2, pDstBuf, line_get_len());
	TaskMiniRun();
	if (iAlg == ALG_MD5SESS)
	{
		line_start(pDstBuf);
		line_add_data(pHA2, MD5_SIGNATURE_SIZE);
		line_add_char(':');
		line_add_str_with_char(pNonce, ':');
		line_add_str(_cCnonce);
		MD5GenValue(pHA2, pDstBuf, line_get_len());
		TaskMiniRun();
	}
	char2asc_str(pHA1, pHA2, MD5_SIGNATURE_SIZE, FALSE);

	// Generate HA2
	line_start(pDstBuf);
	line_add_str_with_char(pMethod, ':');
	line_add_str(pUri);
	if (iQop == QOP_AUTHINT)
	{
		line_add_char(':');
		line_add_str(_cHEntity);
	}
	MD5GenValue(pMD5Value, pDstBuf, line_get_len());
	TaskMiniRun();
	char2asc_str(pHA2, pMD5Value, MD5_SIGNATURE_SIZE, FALSE);

	// generate final response
	line_start(pDstBuf);
	line_add_str_with_char(pHA1, ':');
	line_add_str_with_char(pNonce, ':');
	if (iQop != QOP_NONE)
	{
		line_add_str_with_char(_cNc, ':');
		line_add_str_with_char(_cCnonce, ':');
		line_add_str_with_char(_cQopType[iQop], ':');
	}
	line_add_str(pHA2);
	MD5GenValue(pMD5Value, pDstBuf, line_get_len());
	TaskMiniRun();
	char2asc_str(pHA2, pMD5Value, MD5_SIGNATURE_SIZE, FALSE);

	// Write response
	line_start(pDstBuf);
	line_add_str("Digest username=\"");
	line_add_str_with_char(pAccount, '"');
	if (bRealm)
	{
		line_add_str(",realm=\"");
		line_add_str_with_char(pRealm, '"');
	}
	if (bNonce)
	{
		line_add_str(",nonce=\"");
		line_add_str_with_char(pNonce, '"');
	}
	line_add_str(",uri=\"");
	line_add_str_with_char(pUri, '"');
	// qop=auth, some system don't allow quote sign qop="auth" here
	if (iQop != QOP_NONE)
	{
		line_add_str(",qop=");
		line_add_str(_cQopType[iQop]);
		line_add_str(",nc=");
		line_add_str(_cNc);
		line_add_str(",cnonce=\"");
		line_add_str_with_char(_cCnonce, '"');
	}
	if (bOpaque)
	{
		line_add_str(",opaque=\"");
		line_add_str_with_char(pOpaque, '"');
	}
	if (iAlg != ALG_NONE)
	{
		line_add_str(",algorithm=");
		line_add_str(_cAlgType[iAlg]);
	}
	line_add_str(",response=\"");
	line_add_str_with_char(pHA2, '"');

	if (pRealm)		free(pRealm);
	if (pNonce)		free(pNonce);
	if (pOpaque)	free(pOpaque);

	line_restore();
}

