// File: isysinfo.cpp
//
// INmSysInfo interface  (system information)

#include "precomp.h"
#include "imanager.h"
#include "cuserdta.hpp"
#include "isysinfo.h"
#include <iappldr.h>
#include <tsecctrl.h>

extern VOID SetBandwidth(UINT uBandwidth);

CNmSysInfo* CNmSysInfo::m_pSysInfo = NULL;

BOOL g_fLoggedOn = FALSE; // Set by NM_SYSOPT_LOGGED_ON

static HRESULT OnGateKeeperNotify(IUnknown *pNmSysNotify, LPVOID code, REFIID riid);


static const IID * g_apiidCP_Manager[] =
{
    {&IID_INmSysInfoNotify}
};

/*  C  N M  S Y S  I N F O  */
/*-------------------------------------------------------------------------
    %%Function: CNmSysInfo

-------------------------------------------------------------------------*/
CNmSysInfo::CNmSysInfo() :
	CConnectionPointContainer(g_apiidCP_Manager, ARRAY_ELEMENTS(g_apiidCP_Manager)),
	m_bstrUserName(NULL)
{
	DbgMsg(iZONE_OBJECTS, "Obj: %08X created CNmSysInfo", this);

	ASSERT(NULL == m_pSysInfo);

	m_pSysInfo = this;
}


CNmSysInfo::~CNmSysInfo(void)
{
	DbgMsg(iZONE_OBJECTS, "Obj: %08X destroyed CNmSysInfo", this);

	SysFreeString(m_bstrUserName);

	m_pSysInfo = NULL;
}


//////////////////////////////////////////////////////////////////////////
//  CNmSysInfo:IUknown

ULONG STDMETHODCALLTYPE CNmSysInfo::AddRef(void)
{
	return RefCount::AddRef();
}
	
ULONG STDMETHODCALLTYPE CNmSysInfo::Release(void)
{
	return RefCount::Release();
}


HRESULT STDMETHODCALLTYPE CNmSysInfo::QueryInterface(REFIID riid, PVOID *ppv)
{
	HRESULT hr = S_OK;

	if ((riid == IID_INmSysInfo2) || (riid == IID_INmSysInfo) || (riid == IID_IUnknown))
	{
		*ppv = (INmSysInfo *)this;
		ApiDebugMsg(("CNmSysInfo::QueryInterface()"));
	}
	else if (riid == IID_IConnectionPointContainer)
	{
		*ppv = (IConnectionPointContainer *) this;
		DbgMsgApi("CNmSysInfo::QueryInterface(): Returning IConnectionPointContainer.");
	}
	else
	{
		hr = E_NOINTERFACE;
		*ppv = NULL;
		ApiDebugMsg(("CNmSysInfo::QueryInterface(): Called on unknown interface."));
	}

	if (S_OK == hr)
	{
		AddRef();
	}

	return hr;
}



//////////////////////////////////////////////////////////////////////////
// INmSysInfo

HRESULT STDMETHODCALLTYPE CNmSysInfo::IsInstalled(void)
{
	// TODO: GetLaunchInfo isn't useful for in-proc
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::GetProperty(NM_SYSPROP uProp, BSTR *pbstrProp)
{
	return E_NOTIMPL;
}	


HRESULT STDMETHODCALLTYPE CNmSysInfo::SetProperty(NM_SYSPROP uProp, BSTR bstrData)
{
	switch (uProp)
	{
		case NM_SYSPROP_USER_NAME:
			SysFreeString(m_bstrUserName);
			m_bstrUserName = SysAllocString(bstrData);

			if (NULL != g_pH323UI)
			{
				g_pH323UI->SetUserName(bstrData);
			}
			return S_OK;

		default:
			break;
	}

	return E_INVALIDARG;
}


HRESULT STDMETHODCALLTYPE CNmSysInfo::GetUserData(REFGUID rguid, BYTE **ppb, ULONG *pcb)
{
	return m_UserData.GetUserData(rguid,ppb,pcb);
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::SetUserData(REFGUID rguid, BYTE *pb, ULONG cb)
{
	return m_UserData.AddUserData((GUID *)&rguid,(unsigned short)cb,pb);
}


HRESULT STDMETHODCALLTYPE CNmSysInfo::GetNmApp(REFGUID rguid,
		BSTR *pbstrApplication, BSTR *pbstrCommandLine, BSTR *pbstrDirectory)
{
	return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::SetNmApp(REFGUID rguid,
		BSTR bstrApplication, BSTR bstrCommandLine, BSTR bstrDirectory)
{
	return E_NOTIMPL;
}


HRESULT STDMETHODCALLTYPE CNmSysInfo::GetNmchCaps(ULONG *pchCaps)
{
	return E_NOTIMPL;
}


HRESULT STDMETHODCALLTYPE CNmSysInfo::GetLaunchInfo(INmConference **ppConference, INmMember **ppMember)
{
	return E_NOTIMPL;
}


//////////////////////////////////////////////////////////////////////////
// INmSysInfo3

HRESULT STDMETHODCALLTYPE CNmSysInfo::GetOption(NM_SYSOPT uOption, ULONG * plValue)
{
	return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::SetOption(NM_SYSOPT uOption, ULONG lValue)
{
	IAudioDevice *pAudioDevice = NULL;
	IMediaChannelBuilder *pMCB = NULL;

	switch (uOption)
		{
	case NM_SYSOPT_BANDWIDTH:
	{
		::SetBandwidth(lValue);
		return S_OK;
	}
	
	case NM_SYSOPT_CAPTURE_DEVICE:
	{
		if (NULL == g_pH323UI)
			return E_FAIL;

		g_pH323UI->SetCaptureDevice(lValue);
		return S_OK;
	}
	
	case NM_SYSOPT_LOGGED_ON:
	{
		g_fLoggedOn = lValue;
		return S_OK;
	}

	case NM_SYSOPT_DIRECTSOUND:
	{
		if (NULL == g_pH323UI)
			return E_FAIL;

		pMCB = g_pH323UI->GetStreamProvider();

		if (pMCB)
		{
			pMCB->QueryInterface(IID_IAudioDevice, (void**)&pAudioDevice);
			pAudioDevice->SetDirectSound((BOOL)lValue);
			pAudioDevice->Release();
			pMCB->Release();
			return S_OK;
		}
	}

	case NM_SYSOPT_FULLDUPLEX:
	{
		if (NULL == g_pH323UI)
			return E_FAIL;

		pMCB = g_pH323UI->GetStreamProvider();
		if (pMCB)
		{
			pMCB->QueryInterface(IID_IAudioDevice, (void**)&pAudioDevice);
			pAudioDevice->SetDuplex((BOOL)lValue);  // true==full, false==half
			pAudioDevice->Release();
			pMCB->Release();
			return S_OK;
		}
	}

	default:
		break;
	}

	return E_INVALIDARG;
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::ProcessSecurityData(DWORD dwTaskCode, DWORD_PTR dwParam1, DWORD_PTR dwParam2,
    	DWORD * pdwResult)
{
    switch (dwTaskCode)
    {
        case LOADFTAPPLET:
        {
            ::T120_LoadApplet(APPLET_ID_FT, TRUE, 0, FALSE, NULL);
            return S_OK;
        }
        case UNLOADFTAPPLET:
        {
            ::T120_CloseApplet(APPLET_ID_FT, TRUE, TRUE, 600);
            return S_OK;
        }
        default:
        {
            if (NULL != pdwResult) {
				(* pdwResult) = ::T120_TprtSecCtrl(dwTaskCode, dwParam1, dwParam2);
				return S_OK;
            }
            else {
				return E_FAIL;
            }
        }
    }
}


//////////////////////////////////////////////////////////////////////////
// Gatekeeper / Alias routines

HRESULT STDMETHODCALLTYPE CNmSysInfo::GkLogon(BSTR bstrServer, BSTR bstrAliasID, BSTR bstrAliasE164)
{
	TRACE_OUT(("Gatekeeper Logon: Server=[%ls] AliasID=[%ls] AliasE164=[%ls]",
		bstrServer,
		bstrAliasID ? bstrAliasID : L"<NULL>",
		bstrAliasE164 ? bstrAliasE164 : L"<NULL>"));

	HRESULT hr = E_FAIL;
	
	if (NULL != g_pH323UI)
	{
		IH323CallControl * pH323CallControl = g_pH323UI->GetH323CallControl();
		if (NULL != pH323CallControl)
		{
			SOCKADDR_IN sin;
			sin.sin_family = AF_INET;
			sin.sin_addr.s_addr = inet_addr(CUSTRING(bstrServer));

			// If inet_addr failed, this may be a host address, so try to
			// resolve it

			if (INADDR_NONE == sin.sin_addr.s_addr)
			{
				HOSTENT *host_info;
				
				if ( NULL != (host_info = gethostbyname(CUSTRING(bstrServer))))
				{
					// Only expecting IP addresses..
					ASSERT(( host_info->h_addrtype == AF_INET ));
					ASSERT(( host_info->h_length == sizeof(DWORD)));

					sin.sin_addr.s_addr = *((DWORD *)host_info->h_addr);
				}
			}

			if ((INADDR_NONE != sin.sin_addr.s_addr)
			 && (INADDR_ANY != sin.sin_addr.s_addr))
			{
				H323ALIASLIST AliasList;
				H323ALIASNAME AliasNames[2];
				UINT nAliases = 0;
				UINT nLen;
				
				nLen = SysStringLen(bstrAliasID);
				if (nLen > 0)
				{
					AliasNames[nAliases].aType = AT_H323_ID;
					AliasNames[nAliases].lpwData = bstrAliasID;
					AliasNames[nAliases].wDataLength = (WORD)nLen;// # of unicode chars, w/o NULL terminator
					++nAliases;
				}
				nLen = SysStringLen(bstrAliasE164);
				if (nLen > 0)
				{
					AliasNames[nAliases].aType = AT_H323_E164;
					AliasNames[nAliases].lpwData = bstrAliasE164;
					AliasNames[nAliases].wDataLength = (WORD)nLen;// # of unicode chars, w/o NULL terminator
					++nAliases;
				}
				AliasList.wCount = (WORD)nAliases;
				AliasList.pItems = AliasNames;

				hr = pH323CallControl->EnableGatekeeper(TRUE, &sin, &AliasList, CNmSysInfo::RasNotify);
				if (SUCCEEDED(hr))
				{
					// keep a global copy of the Getkeeper SOCKADDR_IN
					g_sinGateKeeper = sin;
				}
			}
		}
	}
	
	return hr;
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::GkLogoff(void)
{
	TRACE_OUT(("Gatekeeper Logoff"));

	HRESULT hr = S_OK;

	if (NULL != g_pH323UI)
	{
		IH323CallControl * pH323CallControl = g_pH323UI->GetH323CallControl();
		if (NULL != pH323CallControl)
		{
			hr = pH323CallControl->EnableGatekeeper(FALSE,  NULL, NULL, CNmSysInfo::RasNotify);
			if (SUCCEEDED(hr))
			{
				// invalidate the global Getkeeper SOCKADDR_IN
				g_sinGateKeeper.sin_addr.s_addr = INADDR_NONE;
			}
		}
	}


	return hr;
}

HRESULT STDMETHODCALLTYPE CNmSysInfo::GkState(NM_GK_STATE * pgkState)
{
	if (NULL == pgkState)
		return E_POINTER;

	*pgkState = NM_GK_INVALID;
	return E_NOTIMPL;
}


VOID CALLBACK CNmSysInfo::RasNotify(DWORD dwRasEvent, HRESULT hReason)
{
	
	NM_GK_NOTIFY_CODE code = NM_GKNC_INVALID;

	switch(dwRasEvent)
	{
		case RAS_REG_TIMEOUT:	// GK did not respond.  (no hReason)
			code = NM_GKNC_LOGON_TIMEOUT;
			break;

		case RAS_REG_CONFIRM:	// received RCF (registration confirmed)  (no hReason)
			code = NM_GKNC_REG_CONFIRM;
			break;

		case RAS_UNREG_CONFIRM: // received UCF (unregistration confirmed)  (no hReason)
			code = NM_GKNC_UNREG_CONFIRM;
			break;

		case RAS_REJECTED:		// received RRJ (registration rejected)
			
			code = NM_GKNC_REJECTED;

			ASSERT(CUSTOM_FACILITY(hReason) == FACILITY_GKIREGISTRATION);
			switch(CUSTOM_FACILITY_CODE(hReason))
			{
				case RRJ_DISCOVERY_REQ:					TRACE_OUT(("NmSysInfo::RasNotify: GateKeeper logon failed with code RRJ_DISCOVERY_REQ"));
					break;

				case RRJ_INVALID_REVISION:				TRACE_OUT(("GateKeeper logon failed with code RRJ_INVALID_REVISION"));
					break;

				case RRJ_INVALID_CALL_ADDR:				TRACE_OUT(("GateKeeper logon failed with code RRJ_INVALID_CALL_ADDR"));
					break;

				case RRJ_INVALID_RAS_ADDR:				TRACE_OUT(("GateKeeper logon failed with code RRJ_INVALID_RAS_ADDR"));
					break;	

				case RRJ_DUPLICATE_ALIAS:				TRACE_OUT(("GateKeeper logon failed with code RRJ_DUPLICATE_ALIAS"));
					break;

				case RRJ_INVALID_TERMINAL_TYPE:			TRACE_OUT(("GateKeeper logon failed with code RRJ_INVALID_TERMINAL_TYPE"));
					break;

				case RRJ_UNDEFINED:						TRACE_OUT(("GateKeeper logon failed with code RRJ_UNDEFINED"));
					break;

				case RRJ_TRANSPORT_NOT_SUPPORTED:		TRACE_OUT(("GateKeeper logon failed with code RRJ_TRANSPORT_NOT_SUPPORTED"));
					break;

				case RRJ_TRANSPORT_QOS_NOT_SUPPORTED:	TRACE_OUT(("GateKeeper logon failed with code RRJ_TRANSPORT_QOS_NOT_SUPPORTED"));
					break;

				case RRJ_RESOURCE_UNAVAILABLE:			TRACE_OUT(("GateKeeper logon failed with code RRJ_RESOURCE_UNAVAILABLE"));
					break;

				case RRJ_INVALID_ALIAS:					TRACE_OUT(("GateKeeper logon failed with code RRJ_INVALID_ALIAS"));
					break;

				case RRJ_SECURITY_DENIAL:				TRACE_OUT(("GateKeeper logon failed with code RRJ_SECURITY_DENIAL"));
					break;

				default:
				break;
			}
			
		break;
		case RAS_UNREG_REQ:		// received URQ
			code = NM_GKNC_UNREG_REQ;
		// (unregistration request - means that gatekeeper booted the endpoint off)
			ASSERT(CUSTOM_FACILITY(hReason) == FACILITY_GKIUNREGREQ);
			switch(CUSTOM_FACILITY_CODE(hReason))
			{
				case URQ_REREG_REQUIRED:	// GK wants another registration
				case URQ_TTL_EXPIRED:		// TimeToLive expired
				case URQ_SECURITY_DENIAL:	
				case URQ_UNDEFINED:
				default:
				break;
			}
		break;
		default:
		break;
	}

	if( NM_GKNC_INVALID != code )
	{
		if (m_pSysInfo)
		{
			m_pSysInfo->NotifySink(reinterpret_cast<LPVOID>(code), OnGateKeeperNotify);
		}
	}
}

HRESULT OnGateKeeperNotify(IUnknown *pNmSysNotify, LPVOID code, REFIID riid)
{
	HRESULT hr = S_OK;

	if( riid == IID_INmSysInfoNotify )
	{
		NM_GK_NOTIFY_CODE gknc = (NM_GK_NOTIFY_CODE)((DWORD_PTR)(code));
		static_cast<INmSysInfoNotify*>(pNmSysNotify)->GateKeeperNotify( gknc );
	}

	return hr;
}

//////////////////////////////////////////////////////////////////////////
// Internal Methods

HRESULT STDMETHODCALLTYPE CNmSysInfo::GetUserDataList(ULONG * pnRecords, GCCUserData *** pppUserData)
{
	unsigned short nsRecords;
	HRESULT hr = m_UserData.GetUserDataList(&nsRecords,pppUserData);
	*pnRecords = nsRecords;
	return hr;
}