// Cwmsoapauth2.cpp : Implementation of Cwmsoapauth2

#include "stdafx.h"
#include "wmsoapauth2.h"

#include "..\soap_source\wmmd5.h"
#include "..\soap_source\bytearrayconv.h"
#include "..\soap_source\hexcode.h"
#include "..\soap_source\soap.h"
#include <time.h>
#include <comdef.h>

#include "..\wmsoapmsg\wmsoapmsg_i.c"
#include "..\wmsoapstruct\wmsoapstruct.h"
#include "..\wmsoapstruct\wmsoapstruct_i.c"

// Cwmsoapauth2

static wchar_t *g_pwszHeaderNS = L"http://soap-authentication.org/digest/2001/10/";

bool Cwmsoapauth2::Authenticate(Iwmsoapmsg2 *pIwmsoapmsg)
{
	if (ValidateRealmUser(pIwmsoapmsg) == false)
	{
		return false;
	}
	// check nonce aging
	if (CheckNonceAging() == false)
	{
		_AtlModule.LogEvent("Expired Server Nonce in request");
		m_strAuthStatus = "Unauthenticated.ExpiredNonce";
		return false;
	}
	// is response correct?
	unsigned char Expected[MD5_HASH_SIZE];
	CalcDigest(Expected);
	if (memcmp(Expected, m_Auth, MD5_HASH_SIZE) != 0)
	{
		_AtlModule.LogEvent("Invalid Client response in request");
		m_strAuthStatus = "Unauthenticated.InvalidResponse";
		return false;
	}
	m_strAuthStatus = "Authenticated";
	return true;
}

bool Cwmsoapauth2::Response(Iwmsoapmsg2 *pIwmsoapmsg)
{
	// fill up name array
	BSTR bstrNames[4];
	bstrNames[0] = SysAllocString(L"Status");
	bstrNames[1] = SysAllocString(L"Nonce");
	bstrNames[2] = SysAllocString(L"ClientNonce");
	bstrNames[3] = SysAllocString(L"ServerAuth");

	// instantiate struct object
	Iwmsoapstruct *pIwmsoapstructOut;
	HRESULT res = CoCreateInstance( CLSID_wmsoapstruct,
									NULL, CLSCTX_SERVER,
									IID_Iwmsoapstruct,
									(void **) &pIwmsoapstructOut);
	if (FAILED(res))
	{
		return false;
	}
	VARIANT_BOOL bResult;
	_variant_t varTemp;
	for (int j = 0;	j < 4; j++)
	{
		switch (j)
		{
			case 0:
				{
					// Status
					varTemp = m_strAuthStatus.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
				}
				break;
			case 1:
				{
					EnterCriticalSection(&_AtlModule.m_CSNonces);
					// update map - delete entry whose key is old sn
					map<string, WMNonce *>::iterator iM;
					if ((iM = _AtlModule.m_mapNonces.find(m_strSN)) != _AtlModule.m_mapNonces.end())
					{
						delete (*iM).second;
						_AtlModule.m_mapNonces.erase(iM);
					}
					// New server nonce
					WMNonce *pNonce = new WMNonce;
					NewNonce(pNonce);
					// convert to hex encoded string
					m_strSN.erase();
					WMHexCode hexCode;
					hexCode.Encode(pNonce->Nonce, MD5_HASH_SIZE, m_strSN);
					// set struct member
					varTemp = m_strSN.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					// update map - insert entry whose key is new sn
					_AtlModule.m_mapNonces[m_strSN] = pNonce;
					LeaveCriticalSection(&_AtlModule.m_CSNonces);
				}
				break;
			case 2:
				{
					if (m_bCNFound == true)
					{
						// Client nonce
						varTemp = m_strCN.c_str();
						pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					}
				}
				break;
			case 3:
				{
					if (m_bCNFound == true)
					{
						// Server auth token - calculated using the new sn
						unsigned char ServerAuth[MD5_HASH_SIZE];
						CalcDigest(ServerAuth);
						SAFEARRAY *pArray = ByteArrayFromBuffer(ServerAuth, MD5_HASH_SIZE);
						varTemp.parray = pArray;
						varTemp.vt = VT_ARRAY | VT_UI1;
						pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					}
				}
				break;
		}
	}
	// now get IDispatch pointer
	IDispatch *pIDispatchOut;
	res = pIwmsoapstructOut->QueryInterface(IID_IDispatch, (void **)&pIDispatchOut);
	if (FAILED(res))
	{
		for (int j = 0; j < 4; j++)
		{
			SysFreeString(bstrNames[j]);
		}
		pIwmsoapstructOut->Release();
		return false;
	}
	// release
	pIwmsoapstructOut->Release();
	// set up variant
	_variant_t varContent;
	varContent.vt = VT_DISPATCH;
	varContent.pdispVal = pIDispatchOut;
	// add 'NextChallenge' header element
	BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS);
	BSTR bstrLocalName = SysAllocString(L"NextChallenge");
	VARIANT_BOOL bMustUnderstand = VARIANT_TRUE;
	BSTR bstrActorURI = SysAllocString(L"");
	// Is this a SOAP Fault message?
	pIwmsoapmsg->IsSOAPFault(&bResult);
	if (bResult == VARIANT_FALSE)
	{
		pIwmsoapmsg->AddHeaderElem( bstrNamespaceURI,
									bstrLocalName,
									varContent,
									bMustUnderstand,
									bstrActorURI,
									&bResult);
	}
	else
	{
		pIwmsoapmsg->AddFaultHeaderElem(bstrNamespaceURI,
										bstrLocalName,
										varContent,
										bMustUnderstand,
										bstrActorURI,
										&bResult);
	}
	SysFreeString(bstrNamespaceURI);
	SysFreeString(bstrLocalName);
	SysFreeString(bstrActorURI);
	// done with names
	for (j = 0; j < 4; j++)
	{
		SysFreeString(bstrNames[j]);
	}
	if (bResult == VARIANT_FALSE)
	{
		return false;
	}
	return true;
}

bool Cwmsoapauth2::Challenge(Iwmsoapmsg2 *pIwmsoapmsg)
{
	// fill up name array
	BSTR bstrNames[3];
	bstrNames[0] = SysAllocString(L"Status");
	bstrNames[1] = SysAllocString(L"Nonce");
	bstrNames[2] = SysAllocString(L"Realm");

	// instantiate struct object
	Iwmsoapstruct *pIwmsoapstructOut;
	HRESULT res = CoCreateInstance( CLSID_wmsoapstruct,
									NULL, CLSCTX_SERVER,
									IID_Iwmsoapstruct,
									(void **) &pIwmsoapstructOut);
	if (FAILED(res))
	{
		return false;
	}
	VARIANT_BOOL bResult;
	_variant_t varTemp;
	for (int j = 0;	j < 3; j++)
	{
		switch (j)
		{
			case 0:
				{
					// Status
					varTemp = m_strAuthStatus.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
				}
				break;
			case 1:
				{
					EnterCriticalSection(&_AtlModule.m_CSNonces);
					// update map - delete entry whose key is old sn
					map<string, WMNonce *>::iterator iM;
					if ((iM = _AtlModule.m_mapNonces.find(m_strSN)) != _AtlModule.m_mapNonces.end())
					{
						delete (*iM).second;
						_AtlModule.m_mapNonces.erase(iM);
					}
					// New Server nonce
					WMNonce *pNonce = new WMNonce;
					NewNonce(pNonce);
					// convert to string
					m_strSN.erase();
					WMHexCode hexCode;
					hexCode.Encode(pNonce->Nonce, MD5_HASH_SIZE, m_strSN);
					// set struct member
					varTemp = m_strSN.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					// update map - insert entry whose key is new sn
					_AtlModule.m_mapNonces[m_strSN] = pNonce;
					LeaveCriticalSection(&_AtlModule.m_CSNonces);
				}
				break;
			case 2:
				{
					// Realm name
					BSTR bstrService;
					pIwmsoapmsg->GetServiceName(&bstrService);
					// see if service name is in map
					USES_CONVERSION;
					map<string, string>::iterator iM;
					if ((iM = _AtlModule.m_mapServices.find(OLE2A(bstrService))) == _AtlModule.m_mapServices.end())
					{
						// not in map... use default
						m_strRealm = _AtlModule.m_strDefaultRealm;
					}
					else
					{
						// found it
						m_strRealm = (*iM).second;
					}
					SysFreeString(bstrService);
					varTemp = m_strRealm.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
				}
				break;
		}
	}
	// now get IDispatch pointer
	IDispatch *pIDispatchOut;
	res = pIwmsoapstructOut->QueryInterface(IID_IDispatch, (void **)&pIDispatchOut);
	if (FAILED(res))
	{
		for (int j = 0; j < 3; j++)
		{
			SysFreeString(bstrNames[j]);
		}
		pIwmsoapstructOut->Release();
		return false;
	}
	// release
	pIwmsoapstructOut->Release();
	// set up variant
	_variant_t varContent;
	varContent.vt = VT_DISPATCH;
	varContent.pdispVal = pIDispatchOut;
	// add 'AuthSCF' header element
	BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS);
	BSTR bstrLocalName = SysAllocString(L"Challenge");
	VARIANT_BOOL bMustUnderstand = VARIANT_TRUE;
	BSTR bstrActorURI = SysAllocString(L"");
	pIwmsoapmsg->AddFaultHeaderElem(bstrNamespaceURI,
									bstrLocalName,
									varContent,
									bMustUnderstand,
									bstrActorURI,
									&bResult);
	SysFreeString(bstrNamespaceURI);
	SysFreeString(bstrLocalName);
	SysFreeString(bstrActorURI);
	// done with names
	for (j = 0; j < 3; j++)
	{
		SysFreeString(bstrNames[j]);
	}
	if (bResult == VARIANT_FALSE)
	{
		return false;
	}
	return true;
}

bool Cwmsoapauth2::InitChallenge(Iwmsoapmsg2 *pIwmsoapmsg)
{
	if (ValidateRealmUser(pIwmsoapmsg) == false)
	{
		return false;
	}
	// fill up name array
	BSTR bstrNames[4];
	bstrNames[0] = SysAllocString(L"Status");
	bstrNames[1] = SysAllocString(L"Nonce");
	bstrNames[2] = SysAllocString(L"ClientNonce");
	bstrNames[3] = SysAllocString(L"ServerAuth");

	// instantiate struct object
	Iwmsoapstruct *pIwmsoapstructOut;
	HRESULT res = CoCreateInstance( CLSID_wmsoapstruct,
									NULL, CLSCTX_SERVER,
									IID_Iwmsoapstruct,
									(void **) &pIwmsoapstructOut);
	if (FAILED(res))
	{
		return false;
	}
	VARIANT_BOOL bResult;
	_variant_t varTemp;
	for (int j = 0;	j < 4; j++)
	{
		switch (j)
		{
			case 0:
				{
					// Status
					varTemp = m_strAuthStatus.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
				}
				break;
			case 1:
				{
					// New server nonce
					WMNonce *pNonce = new WMNonce;
					NewNonce(pNonce);
					// convert to hex encoded string
					m_strSN.erase();
					WMHexCode hexCode;
					hexCode.Encode(pNonce->Nonce, MD5_HASH_SIZE, m_strSN);
					// set struct member
					varTemp = m_strSN.c_str();
					pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					// update map - insert entry whose key is new sn
					EnterCriticalSection(&_AtlModule.m_CSNonces);
					_AtlModule.m_mapNonces[m_strSN] = pNonce;
					LeaveCriticalSection(&_AtlModule.m_CSNonces);
				}
				break;
			case 2:
				{
					if (m_bCNFound == true)
					{
						// Client nonce
						varTemp = m_strCN.c_str();
						pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					}
				}
				break;
			case 3:
				{
					if (m_bCNFound == true)
					{
						// Server auth token - calculated using the new sn
						unsigned char ServerAuth[MD5_HASH_SIZE];
						CalcDigest(ServerAuth);
						SAFEARRAY *pArray = ByteArrayFromBuffer(ServerAuth, MD5_HASH_SIZE);
						varTemp.parray = pArray;
						varTemp.vt = VT_ARRAY | VT_UI1;
						pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult);
					}
				}
				break;
		}
	}
	// now get IDispatch pointer
	IDispatch *pIDispatchOut;
	res = pIwmsoapstructOut->QueryInterface(IID_IDispatch, (void **)&pIDispatchOut);
	if (FAILED(res))
	{
		for (int j = 0; j < 4; j++)
		{
			SysFreeString(bstrNames[j]);
		}
		pIwmsoapstructOut->Release();
		return false;
	}
	// release
	pIwmsoapstructOut->Release();
	// set up variant
	_variant_t varContent;
	varContent.vt = VT_DISPATCH;
	varContent.pdispVal = pIDispatchOut;
	// add 'NextChallenge' header element
	BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS);
	BSTR bstrLocalName = SysAllocString(L"NextChallenge");
	VARIANT_BOOL bMustUnderstand = VARIANT_TRUE;
	BSTR bstrActorURI = SysAllocString(L"");
	// this a SOAP Fault message
	pIwmsoapmsg->AddFaultHeaderElem(bstrNamespaceURI,
									bstrLocalName,
									varContent,
									bMustUnderstand,
									bstrActorURI,
									&bResult);
	SysFreeString(bstrNamespaceURI);
	SysFreeString(bstrLocalName);
	SysFreeString(bstrActorURI);
	// done with names
	for (j = 0; j < 4; j++)
	{
		SysFreeString(bstrNames[j]);
	}
	if (bResult == VARIANT_FALSE)
	{
		return false;
	}
	return true;
}

bool Cwmsoapauth2::ValidateRealmUser(Iwmsoapmsg2 *pIwmsoapmsg)
{
	// check if realm name is valid
	BSTR bstrService;
	pIwmsoapmsg->GetServiceName(&bstrService);
	string strTemp;
	// see if service name is in map
	USES_CONVERSION;
	map<string, string>::iterator iM;
	if ((iM = _AtlModule.m_mapServices.find(OLE2A(bstrService))) == _AtlModule.m_mapServices.end())
	{
		// not in map... use default
		strTemp = _AtlModule.m_strDefaultRealm;
	}
	else
	{
		// found it
		strTemp = (*iM).second;
	}
	SysFreeString(bstrService);
	if (m_strRealm != strTemp)
	{
		_AtlModule.LogEvent("Invalid Realm name in request");
		m_strAuthStatus = "Unauthenticated.InvalidRealm";
		return false;
	}
	// check if user id is valid
	m_strAccount = m_strRealm;
	m_strAccount += ":";
	m_strAccount += m_strUserID;
	if (_AtlModule.m_mapUsers.find(m_strAccount) == _AtlModule.m_mapUsers.end())
	{
		_AtlModule.LogEvent("Invalid User ID in request");
		m_strAuthStatus = "Unauthenticated.InvalidUser";
		return false;
	}
	// all is ok
	return true;
}

bool Cwmsoapauth2::NewNonce(WMNonce *pNonce)
{
	// first the time
	pNonce->Time = (long)time(NULL);
	// now the nonce
	GUID g;
	CoCreateGuid(&g);
	WMMD5 md5;
	md5.Update((unsigned char *)&g, sizeof(GUID));
	md5.Finalize((unsigned long *)&pNonce->Nonce[0]);
	return true;
}

bool Cwmsoapauth2::CalcDigest(void *pBuffer)
{
	// calculate proper response
	// if client nonce given = MD5( MD5(uid:realm:pwd):SN:CN )
	// else = MD5( MD5(uid:realm:pwd):SN )
	string strTemp1 = m_strUserID;
	strTemp1 += ":";
	strTemp1 += m_strRealm;
	strTemp1 += ":";
	strTemp1 += _AtlModule.m_mapUsers[m_strAccount];
	string strTemp2;

	WMMD5 md5;
	md5.Update((unsigned char *)strTemp1.c_str(), strTemp1.length());
	md5.FinalizeHexEncoded(strTemp2);

	strTemp1 = strTemp2;
	strTemp1 += ":";
	// server nonce string
	strTemp1 += m_strSN;
	if (m_bCNFound == true)
	{
		// incorporate client nonce
		strTemp1 += ":";
		strTemp1 += m_strCN;
	}
	md5.Update((unsigned char *)strTemp1.c_str(), strTemp1.length());
	md5.Finalize((unsigned long *)pBuffer);
	return true;
}

bool Cwmsoapauth2::CheckNonceAging()
{
	EnterCriticalSection(&_AtlModule.m_CSNonces);
	map<string, WMNonce *>::iterator iM;
	if ((iM = _AtlModule.m_mapNonces.find(m_strSN)) == _AtlModule.m_mapNonces.end())
	{
		LeaveCriticalSection(&_AtlModule.m_CSNonces);
		return false;
	}
	WMNonce *pNonce = (*iM).second;
	if (pNonce == NULL)
	{
		LeaveCriticalSection(&_AtlModule.m_CSNonces);
		return false;
	}
	time_t t = pNonce->Time;
	LeaveCriticalSection(&_AtlModule.m_CSNonces);
	// stale if nonce life is exceeded
	if ((time(NULL) - t) > _AtlModule.m_nNonceLife)
	{
		return false;
	}
	return true;
}

STDMETHODIMP Cwmsoapauth2::OnProcessHeader(VARIANT SOAPMsgInterface, VARIANT_BOOL *pResult)
{
	// get interface ptr
	IDispatch *pIDispatch = SOAPMsgInterface.pdispVal;
	Iwmsoapmsg2 *pIwmsoapmsg;
	HRESULT hRes = pIDispatch->QueryInterface(IID_Iwmsoapmsg2, (void **)&pIwmsoapmsg);
	if (hRes != S_OK)
	{
		return E_FAIL;
	}
	VARIANT_BOOL bResult;
	pIwmsoapmsg->IsRequestPhase(&bResult);
	if (bResult == VARIANT_FALSE)
	{
		// this is response phase
		*pResult = VARIANT_TRUE;
		if (m_bAuthSucceeded == true)
		{
			// post response header entry
			*pResult = Response(pIwmsoapmsg) == true ? VARIANT_TRUE : VARIANT_FALSE;
		}
		pIwmsoapmsg->Release();
		return S_OK;
	}
	// request phase... assume the worst
	m_bAuthSucceeded = false;
	m_bAuthFound = false;
	m_bUserIDFound = false;
	m_bRealmFound = false;
	m_bCNFound = false;
	m_bSNFound = false;
	m_strAuthStatus = "Unauthenticated.NoCredentials";
	*pResult = VARIANT_FALSE;
	_variant_t varContent;
	bool bClientAuthFound = false;
	bool bInitChallengeFound = false;

	// is the 'ClientAuth' header entry present?
	BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS);
	BSTR bstrLocalName = SysAllocString(L"ClientAuth");
	hRes = pIwmsoapmsg->GetHeaderElem(bstrNamespaceURI, bstrLocalName, &varContent, &bResult);
	SysFreeString(bstrLocalName);
	if (hRes == S_OK && bResult == VARIANT_TRUE && (varContent.vt & VT_TYPEMASK) == VT_DISPATCH)
	{
		bClientAuthFound = true;
	}
	else
	{
		// is the 'InitChallenge' header entry present?
		bstrLocalName = SysAllocString(L"InitChallenge");
		hRes = pIwmsoapmsg->GetHeaderElem(bstrNamespaceURI, bstrLocalName, &varContent, &bResult);
		SysFreeString(bstrLocalName);
		if (hRes == S_OK && bResult == VARIANT_TRUE && (varContent.vt & VT_TYPEMASK) == VT_DISPATCH)
		{
			bInitChallengeFound = true;
		}
	}
	SysFreeString(bstrNamespaceURI);
	// extract info from any header entry found
	if (bClientAuthFound == true)
	{
		// fill up name array
		BSTR bstrNames[5];
		bstrNames[0] = SysAllocString(L"Nonce");
		bstrNames[1] = SysAllocString(L"Auth");
		bstrNames[2] = SysAllocString(L"UserID");
		bstrNames[3] = SysAllocString(L"Realm");
		bstrNames[4] = SysAllocString(L"ClientNonce");

		IDispatch *pIDispatch = varContent.pdispVal;
		Iwmsoapstruct *pIwmsoapstruct;
		HRESULT res = pIDispatch->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstruct);
		if (FAILED(res))
		{
			for (int i = 0; i < 5; i++)
			{
				SysFreeString(bstrNames[i]);
			}
			return S_OK;
		}
		USES_CONVERSION;
		_variant_t varTemp;
		VARIANT_BOOL bResult;
		// now iterate over struct members
		for (int j = 0;	j < 5; j++)
		{
			// get member
			pIwmsoapstruct->GetMember(bstrNames[j], &varTemp, &bResult);

			switch (j)
			{
				case 0:
					// Server Nonce
					if (bResult == VARIANT_TRUE)
					{
						m_strSN = OLE2A(varTemp.bstrVal);
						m_bSNFound = true;
					}
					break;
				case 1:
					// Auth token
					if (bResult == VARIANT_TRUE)
					{
						void *pBuffer;
						unsigned long lSize;
						if (BufferFromByteArray(varTemp.parray,
												&pBuffer,
												&lSize) == true)
						{
							memmove(m_Auth, pBuffer, MD5_HASH_SIZE);
							free(pBuffer);
							m_bAuthFound = true;
						}
					}
					break;
				case 2:
					// User ID
					if (bResult == VARIANT_TRUE)
					{
						m_strUserID = OLE2A(varTemp.bstrVal);
						m_bUserIDFound = true;
					}
					break;
				case 3:
					// Realm
					if (bResult == VARIANT_TRUE)
					{
						m_strRealm = OLE2A(varTemp.bstrVal);
						m_bRealmFound = true;
					}
					break;
				case 4:
					// Client Nonce
					if (bResult == VARIANT_TRUE)
					{
						m_strCN = OLE2A(varTemp.bstrVal);
						m_bCNFound = true;
					}
					break;
			}
			varTemp.Clear();
		}
		// release interface ptr
		pIwmsoapstruct->Release();
		// done with names
		for (j = 0; j < 5; j++)
		{
			SysFreeString(bstrNames[j]);
		}
	}
	else if (bInitChallengeFound == true)
	{
		// fill up name array
		BSTR bstrNames[3];
		bstrNames[0] = SysAllocString(L"UserID");
		bstrNames[1] = SysAllocString(L"Realm");
		bstrNames[2] = SysAllocString(L"ClientNonce");

		IDispatch *pIDispatch = varContent.pdispVal;
		Iwmsoapstruct *pIwmsoapstruct;
		HRESULT res = pIDispatch->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstruct);
		if (FAILED(res))
		{
			for (int i = 0; i < 3; i++)
			{
				SysFreeString(bstrNames[i]);
			}
			return S_OK;
		}
		USES_CONVERSION;
		_variant_t varTemp;
		VARIANT_BOOL bResult;
		// now iterate over struct members
		for (int j = 0;	j < 5; j++)
		{
			// get member
			pIwmsoapstruct->GetMember(bstrNames[j], &varTemp, &bResult);

			switch (j)
			{
				case 0:
					// User ID
					if (bResult == VARIANT_TRUE)
					{
						m_strUserID = OLE2A(varTemp.bstrVal);
						m_bUserIDFound = true;
					}
					break;
				case 1:
					// Realm
					if (bResult == VARIANT_TRUE)
					{
						m_strRealm = OLE2A(varTemp.bstrVal);
						m_bRealmFound = true;
					}
					break;
				case 2:
					// Client Nonce
					if (bResult == VARIANT_TRUE)
					{
						m_strCN = OLE2A(varTemp.bstrVal);
						m_bCNFound = true;
					}
					break;
			}
			varTemp.Clear();
		}
		// release interface ptr
		pIwmsoapstruct->Release();
		// done with names
		for (j = 0; j < 3; j++)
		{
			SysFreeString(bstrNames[j]);
		}
	}
	varContent.Clear();

	// now process
	*pResult = VARIANT_TRUE;
	if (bClientAuthFound == true &&
		m_bAuthFound == true &&
		m_bUserIDFound == true &&
		m_bRealmFound == true &&
		m_bSNFound == true)
	{
		// 'ClientAuth' header entry found
		if (Authenticate(pIwmsoapmsg) == true)
		{
			// success... done for now
			m_bAuthSucceeded = true;
			pIwmsoapmsg->Release();
			return S_OK;
		}
		else
		{
			// authentication failed... issue challenge
			Challenge(pIwmsoapmsg);
		}
	}
	else if (   bInitChallengeFound == true &&
				m_bUserIDFound == true &&
				m_bRealmFound == true)
	{
		// 'InitChallenge' header entry found
		if (InitChallenge(pIwmsoapmsg) == false)
		{
			// realm name or user id was invalid... issue challenge
			Challenge(pIwmsoapmsg);
		}
	}
	else
	{
		// no header entry found... issue challenge
		Challenge(pIwmsoapmsg);
	}
    long lSOAPVersion;
    pIwmsoapmsg->GetSOAPVersion(&lSOAPVersion);
	BSTR bstrFaultcode, bstrFaultcodeNS;
	BSTR bstrFaultstring = SysAllocString(L"Authentication failed: missing, malformed, or invalid credentials.");
    if (lSOAPVersion == SOAP_VERSION_1_1)
    {
		bstrFaultcode = SysAllocString(L"Client");
        bstrFaultcodeNS = SysAllocString(g_pwszSoapEnvelopeNS);
		pIwmsoapmsg->SetSOAPFault(bstrFaultcode, bstrFaultcodeNS, bstrFaultstring, VARIANT_FALSE, &bResult);
    }
    else
    {
		bstrFaultcode = SysAllocString(L"Sender");
        bstrFaultcodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
        BSTR bstrSubcode = SysAllocString(L"");
        BSTR bstrSubcodeNS = SysAllocString(L"");
        BSTR bstrLangId = SysAllocString(L"en");
		pIwmsoapmsg->SetSOAP12Fault(bstrFaultcode, bstrFaultcodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultstring, bstrLangId, &bResult);
        SysFreeString(bstrSubcode);
        SysFreeString(bstrSubcodeNS);
        SysFreeString(bstrLangId);
    }
	SysFreeString(bstrFaultcode);
	SysFreeString(bstrFaultcodeNS);
	SysFreeString(bstrFaultstring);

	pIwmsoapmsg->Release();
	return S_OK;
}

STDMETHODIMP Cwmsoapauth2::OnTestMUHeaderBlock(BSTR bstrNamespace, BSTR bstrLocalName, VARIANT_BOOL *pResult)
{
    *pResult = VARIANT_FALSE;
    if (wcscmp(bstrNamespace, g_pwszHeaderNS) == 0)
    {
        if (wcscmp(bstrLocalName, L"ClientAuth") == 0 ||
            wcscmp(bstrLocalName, L"InitChallenge") == 0)
        {
            *pResult = VARIANT_TRUE;
            return S_OK;
        }
    }
    return S_OK;
}
