mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2398 lines
81 KiB
2398 lines
81 KiB
/****************************************************************************/
|
|
/* */
|
|
/* ERNCCM.CPP */
|
|
/* */
|
|
/* Conference Manager class for the Reference System Node Controller. */
|
|
/* */
|
|
/* Copyright Data Connection Ltd. 1995 */
|
|
/* */
|
|
/****************************************************************************/
|
|
/* Changes: */
|
|
/* */
|
|
/* 07Jul95 NFC Created. */
|
|
/* 23Aug95 NFC Bad trace in StartConference(). */
|
|
/* 05Sep95 NFC Integration with CMP_Notify* API. */
|
|
/* 13Sep95 NFC Added handler for GCC_EJECT_USER_INDICATION */
|
|
/* 19Sep95 NFC Missing break in GetConfIDFromMessage(). */
|
|
/****************************************************************************/
|
|
#include "precomp.h"
|
|
DEBUG_FILEZONE(ZONE_GCC_NC);
|
|
#include "ernccons.h"
|
|
#include "nccglbl.hpp"
|
|
#include "erncvrsn.hpp"
|
|
#include "t120app.h"
|
|
#include <cuserdta.hpp>
|
|
#include <confcli.h>
|
|
#include <confreg.h>
|
|
|
|
#include "erncconf.hpp"
|
|
#include "ernccm.hpp"
|
|
#include "ernctrc.h"
|
|
#include <iappldr.h>
|
|
|
|
#include "appldr.h"
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include "plgxprt.h"
|
|
|
|
#ifdef _DEBUG
|
|
BOOL g_fInterfaceBreak = FALSE;
|
|
#endif
|
|
|
|
#define MAX_INVALID_PASSWORDS 5
|
|
|
|
// Global data structures.
|
|
DCRNCConferenceManager *g_pNCConfMgr = NULL;
|
|
CQueryRemoteWorkList *g_pQueryRemoteList = NULL;
|
|
INodeControllerEvents *g_pCallbackInterface = NULL;
|
|
HINSTANCE g_hDllInst = NULL;
|
|
IT120ControlSAP *g_pIT120ControlSap = NULL;
|
|
BOOL g_bRDS = FALSE;
|
|
|
|
extern PController g_pMCSController;
|
|
|
|
// Private function prototypes.
|
|
|
|
void HandleAddInd(AddIndicationMessage * pAddInd);
|
|
void HandleQueryConfirmation(QueryConfirmMessage * pQueryMessage);
|
|
void HandleQueryIndication(QueryIndicationMessage * pQueryMessage);
|
|
void HandleConductGiveInd(ConductGiveIndicationMessage * pConductGiveInd);
|
|
void HandleLockIndication(LockIndicationMessage * pLockInd);
|
|
void HandleUnlockIndication(UnlockIndicationMessage * pUnlockInd);
|
|
void HandleSubInitializedInd(SubInitializedIndicationMessage * pSubInitInd);
|
|
void HandleTimeInquireIndication(TimeInquireIndicationMessage * pTimeInquireInd);
|
|
void HandleApplicationInvokeIndication(ApplicationInvokeIndicationMessage * pInvokeMessage);
|
|
|
|
BOOL InitializePluggableTransport(void);
|
|
void CleanupPluggableTransport(void);
|
|
|
|
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD fdwReason, LPVOID)
|
|
{
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
g_hDllInst = hDllInst;
|
|
ASSERT (g_hDllInst != NULL);
|
|
DisableThreadLibraryCalls (hDllInst);
|
|
DBG_INIT_MEMORY_TRACKING(hDllInst);
|
|
::InitializeCriticalSection(&g_csTransport);
|
|
T120DiagnosticCreate();
|
|
g_bRDS = ( NULL != ::FindAtomA("NMSRV_ATOM"));
|
|
break;
|
|
}
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
{
|
|
g_hDllInst = NULL;
|
|
/*
|
|
* Go cleanup all resources on behalf of the process that is
|
|
* detaching from this DLL.
|
|
*/
|
|
T120DiagnosticDestroy ();
|
|
::DeleteCriticalSection(&g_csTransport);
|
|
DBG_CHECK_MEMORY_TRACKING(hDllInst);
|
|
break;
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
HRESULT WINAPI
|
|
T120_CreateNodeController
|
|
(
|
|
INodeController **ppNodeCtrlIntf,
|
|
INodeControllerEvents *pEventsCallback
|
|
)
|
|
{
|
|
DebugEntry(T120_CreateNodeController);
|
|
|
|
HRESULT hr;
|
|
if (NULL == g_pNCConfMgr)
|
|
{
|
|
if (NULL != ppNodeCtrlIntf && NULL != pEventsCallback)
|
|
{
|
|
*ppNodeCtrlIntf = NULL;
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
if (NULL != (g_pNCConfMgr = new DCRNCConferenceManager(pEventsCallback, &hr)))
|
|
{
|
|
if (S_OK == hr)
|
|
{
|
|
*ppNodeCtrlIntf = (INodeController*) g_pNCConfMgr;
|
|
}
|
|
else
|
|
{
|
|
g_pNCConfMgr->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_T120_ALREADY_INITIALIZED;
|
|
}
|
|
|
|
DebugExitHRESULT(T120_CreateNodeController, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Constructor - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
DCRNCConferenceManager::
|
|
DCRNCConferenceManager
|
|
(
|
|
INodeControllerEvents *pCallback,
|
|
HRESULT *pRetCode
|
|
)
|
|
:
|
|
CRefCount(MAKE_STAMP_ID('N', 'C', 'C', 'M')),
|
|
m_eState(CM_ST_UNINITIALIZED)
|
|
{
|
|
GCCError GCCrc;
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
DebugEntry(DCRNCConferenceManager::DCRNCConferenceManager);
|
|
|
|
::InitializePluggableTransport();
|
|
|
|
//
|
|
// There should be only one NC conference manager in the system.
|
|
//
|
|
ASSERT(NULL == g_pNCConfMgr);
|
|
|
|
ASSERT(pRetCode);
|
|
|
|
// initialize applet loader structure
|
|
::AppLdr_Initialize();
|
|
|
|
//
|
|
// Save the callback interface to nmcom.dll
|
|
//
|
|
g_pCallbackInterface = pCallback;
|
|
|
|
//
|
|
// Validate that there is a node name.
|
|
//
|
|
LPWSTR pwszNodeName;
|
|
if (NULL != (pwszNodeName = ::GetNodeName()))
|
|
{
|
|
delete pwszNodeName;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Failed to obtain node name"));
|
|
hr = UI_RC_NO_NODE_NAME;
|
|
goto MyExit;
|
|
}
|
|
|
|
//
|
|
// Load versioning information.
|
|
//
|
|
hr = ::InitOurVersion();
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("Failed to load version info"));
|
|
goto MyExit;
|
|
}
|
|
|
|
//
|
|
// Create the query-remote list.
|
|
//
|
|
ASSERT(NULL == g_pQueryRemoteList);
|
|
DBG_SAVE_FILE_LINE
|
|
g_pQueryRemoteList = new CQueryRemoteWorkList();
|
|
if (g_pQueryRemoteList == NULL)
|
|
{
|
|
ERROR_OUT(("Failed to create Query Remote List"));
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
goto MyExit;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* For GCCInitialize: */
|
|
/* */
|
|
/* - pass in a pointer to CM as the user defined data, allowing */
|
|
/* GCCCallBackHandler to call back into CM to handle GCC callbacks. */
|
|
/************************************************************************/
|
|
GCCrc = ::T120_CreateControlSAP(&g_pIT120ControlSap, this, GCCCallBackHandler);
|
|
if (GCCrc == GCC_NO_ERROR)
|
|
{
|
|
m_eState = CM_ST_GCC_INITIALIZED;
|
|
hr = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Failed to initializeGCC, GCC error %d", GCCrc));
|
|
hr = ::GetGCCRCDetails(GCCrc);
|
|
}
|
|
|
|
MyExit:
|
|
|
|
*pRetCode = hr;
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::DCRNCConferenceManager, hr);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Destructor - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
DCRNCConferenceManager::
|
|
~DCRNCConferenceManager(void)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::~DCRNCConferenceManager);
|
|
|
|
//
|
|
// Make sure no one can use this global pointer any more since
|
|
// we are deleting this object.
|
|
//
|
|
g_pNCConfMgr = NULL;
|
|
g_pCallbackInterface = NULL;
|
|
|
|
//
|
|
// Release cached version
|
|
//
|
|
::ReleaseOurVersion();
|
|
|
|
//
|
|
// Clean up the query-remote list
|
|
//
|
|
delete g_pQueryRemoteList;
|
|
g_pQueryRemoteList = NULL;
|
|
|
|
//
|
|
// If we have initialized GCC, uninitialize it.
|
|
//
|
|
if (NULL != g_pIT120ControlSap)
|
|
{
|
|
ASSERT(CM_ST_GCC_INITIALIZED == m_eState);
|
|
g_pIT120ControlSap->ReleaseInterface();
|
|
g_pIT120ControlSap = NULL;
|
|
}
|
|
m_eState = CM_ST_UNINITIALIZED;
|
|
|
|
::CleanupPluggableTransport();
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::~DCRNCConferenceManager);
|
|
}
|
|
|
|
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// Implementation of INodeController interface
|
|
//
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
STDMETHODIMP_(void) DCRNCConferenceManager::
|
|
ReleaseInterface ( void )
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::ReleaseInterface);
|
|
InterfaceEntry();
|
|
|
|
// de-initialize applet loader structure
|
|
::AppLdr_Shutdown();
|
|
|
|
//
|
|
// End and delete all the conferences.
|
|
//
|
|
PCONFERENCE pConf;
|
|
while (NULL != (pConf = m_ConfList.Get()))
|
|
{
|
|
RemoveConference(pConf, TRUE, TRUE);
|
|
}
|
|
|
|
//
|
|
// Free the query remote list
|
|
//
|
|
g_pQueryRemoteList->DeleteList();
|
|
|
|
//
|
|
// Empty our sequential lists of entries without owners.
|
|
//
|
|
m_InviteIndWorkList.DeleteList();
|
|
m_JoinIndWorkList.DeleteList();
|
|
|
|
//
|
|
// Reset the NC related data
|
|
//
|
|
g_pCallbackInterface = NULL;
|
|
|
|
//
|
|
// Release this object now.
|
|
//
|
|
Release();
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::ReleaseInterface);
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConferenceManager::
|
|
QueryRemote
|
|
(
|
|
LPVOID pCallerContext,
|
|
LPCSTR pcszNodeAddress,
|
|
BOOL fSecure,
|
|
BOOL bIsConferenceActive
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::QueryRemote);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
#if defined(TEST_PLUGGABLE) && defined(_DEBUG)
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
pcszNodeAddress = ::FakeNodeAddress(pcszNodeAddress);
|
|
}
|
|
#endif
|
|
|
|
if (NULL != pcszNodeAddress)
|
|
{
|
|
// if winsock is disabled, block any IP address or machine name
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
if (! IsValidPluggableTransportName(pcszNodeAddress))
|
|
{
|
|
return UI_RC_NO_WINSOCK;
|
|
}
|
|
}
|
|
|
|
// Construct context for the life of the request.
|
|
DBG_SAVE_FILE_LINE
|
|
CQueryRemoteWork *pQueryRemote;
|
|
DBG_SAVE_FILE_LINE
|
|
pQueryRemote = new CQueryRemoteWork(pCallerContext,
|
|
bIsConferenceActive ? GCC_ASYMMETRY_CALLER : GCC_ASYMMETRY_UNKNOWN,
|
|
// GCC_ASYMMETRY_CALLER, // lonchanc: always want to be the caller
|
|
pcszNodeAddress,
|
|
fSecure,
|
|
&hr);
|
|
if (NULL != pQueryRemote && NO_ERROR == hr)
|
|
{
|
|
//
|
|
// LONCHANC: The following call is to put this query remote work item
|
|
// to the global list, and do the work. We have to do this because
|
|
// we removed the physical connection.
|
|
//
|
|
pQueryRemote->SetHr(NO_ERROR);
|
|
|
|
// Put entry in list of pending query requests to
|
|
// issue GCCConferenceQuery on connection.
|
|
g_pQueryRemoteList->AddWorkItem(pQueryRemote);
|
|
|
|
hr = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::QueryRemote:: can't allocate query remote work item"));
|
|
delete pQueryRemote;
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::QueryRemote:: null pcszAddress"));
|
|
hr = UI_RC_NO_ADDRESS;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::QueryRemote, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConferenceManager::
|
|
CancelQueryRemote ( LPVOID pCallerContext )
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::CancelQueryRemote);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr = g_pQueryRemoteList->Cancel(pCallerContext);
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::CancelQueryRemote, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConferenceManager::
|
|
CreateConference
|
|
(
|
|
LPCWSTR pcwszConfName,
|
|
LPCWSTR pcwszPassword,
|
|
PBYTE pbHashedPassword,
|
|
DWORD cbHashedPassword,
|
|
BOOL fSecure,
|
|
CONF_HANDLE *phConf
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::CreateConference);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
if (NULL != phConf)
|
|
{
|
|
*phConf = NULL;
|
|
if (! ::IsEmptyStringW(pcwszConfName))
|
|
{
|
|
PCONFERENCE pNewConf;
|
|
|
|
/************************************************************************/
|
|
/* Create a new conference. */
|
|
/************************************************************************/
|
|
hr = CreateNewConference(pcwszConfName, NULL, &pNewConf, FALSE, fSecure);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
ASSERT(NULL != pNewConf);
|
|
|
|
/****************************************************************/
|
|
/* Only need the name for a new local conference. */
|
|
/****************************************************************/
|
|
hr = pNewConf->StartLocal(pcwszPassword, pbHashedPassword, cbHashedPassword);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
pNewConf->SetNotifyToDo(TRUE);
|
|
*phConf = (CONF_HANDLE) pNewConf;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::CreateConference: can't start local conference, hr=0x%x", (UINT) hr));
|
|
if (hr != UI_RC_CONFERENCE_ALREADY_EXISTS)
|
|
{
|
|
RemoveConference(pNewConf);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::CreateConference: failed to create new conference, hr=0x%x", (UINT) hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::CreateConference: invalid conference name"));
|
|
hr = UI_RC_NO_CONFERENCE_NAME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::CreateConference: null phConf"));
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::CreateConference, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConferenceManager::
|
|
JoinConference
|
|
(
|
|
LPCWSTR pcwszConfName,
|
|
LPCWSTR pcwszPassword,
|
|
LPCSTR pcszNodeAddress,
|
|
BOOL fSecure,
|
|
USERDATAINFO *pUserDataInfoEntries,
|
|
UINT cUserDataEntries,
|
|
CONF_HANDLE *phConf
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::JoinConference);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
#if defined(TEST_PLUGGABLE) && defined(_DEBUG)
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
pcszNodeAddress = ::FakeNodeAddress(pcszNodeAddress);
|
|
}
|
|
#endif
|
|
|
|
if (NULL != phConf)
|
|
{
|
|
*phConf = NULL;
|
|
if (! ::IsEmptyStringW(pcwszConfName) && NULL != pcszNodeAddress)
|
|
{
|
|
// if winsock is disabled, block any IP address or machine name
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
if (! IsValidPluggableTransportName(pcszNodeAddress))
|
|
{
|
|
return UI_RC_NO_WINSOCK;
|
|
}
|
|
}
|
|
|
|
PCONFERENCE pNewConf;
|
|
|
|
// Create a new conference, or find a new conference that
|
|
// has just rejected a join because of an invalid password,
|
|
// and call its Join() entry point.
|
|
hr = CreateNewConference(pcwszConfName, NULL, &pNewConf, TRUE, fSecure);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
// First join attempt. Do all of the start connection.
|
|
hr = pNewConf->Join((LPSTR) pcszNodeAddress,
|
|
pUserDataInfoEntries,
|
|
cUserDataEntries,
|
|
pcwszPassword);
|
|
}
|
|
else
|
|
if (hr == UI_RC_CONFERENCE_ALREADY_EXISTS)
|
|
{
|
|
// Conference already exists.
|
|
// Look to see if it is awaiting a join with a password.
|
|
// If so, then retry the join.
|
|
// Otherwise drop through to return an error.
|
|
// Note that we walk the list here again to find the existing
|
|
// conference rather than pass back from CreateNewConference(),
|
|
// because that would be a side effect behavior that can (and has!)
|
|
// introduce obscure bugs in unrelated code.
|
|
hr = NO_ERROR;
|
|
pNewConf = GetConferenceFromName(pcwszConfName);
|
|
ASSERT(NULL != pNewConf);
|
|
if (! pNewConf->IsConnListEmpty())
|
|
{
|
|
CLogicalConnection *pConEntry = pNewConf->PeekConnListHead();
|
|
if (pConEntry->GetState() == CONF_CON_PENDING_PASSWORD)
|
|
{
|
|
hr = pNewConf->JoinWrapper(pConEntry, pcwszPassword);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete the conference if the join fails
|
|
// for any reason other than trying to join
|
|
// a local conference.
|
|
if (NO_ERROR == hr)
|
|
{
|
|
pNewConf->SetNotifyToDo(TRUE);
|
|
*phConf = (CONF_HANDLE) pNewConf;
|
|
}
|
|
else
|
|
{
|
|
if (hr != UI_RC_CONFERENCE_ALREADY_EXISTS)
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::JoinConference: Failed to create new conference, hr=0x%x", (UINT) hr));
|
|
}
|
|
RemoveConference(pNewConf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = (pcszNodeAddress == NULL) ? UI_RC_NO_ADDRESS : UI_RC_NO_CONFERENCE_NAME;
|
|
ERROR_OUT(("DCRNCConferenceManager::JoinConference: invalid parameters, hr=0x%x", (UINT) hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::JoinConference: null phConf"));
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::JoinConference, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConferenceManager::
|
|
GetUserData
|
|
(
|
|
ROSTER_DATA_HANDLE hUserData,
|
|
const GUID *pcGUID,
|
|
UINT *pcbData,
|
|
LPVOID *ppvData
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::GetUserData);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
GCCNodeRecord * pRosterEntry = (GCCNodeRecord *) hUserData;
|
|
|
|
if (NULL != pRosterEntry)
|
|
{
|
|
ASSERT(NULL != pcbData);
|
|
hr = ::GetUserData(pRosterEntry->number_of_user_data_members,
|
|
pRosterEntry->user_data_list,
|
|
(GUID*) pcGUID,
|
|
pcbData,
|
|
ppvData);
|
|
if (NO_ERROR != hr && UI_RC_NO_SUCH_USER_DATA != hr)
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::GetUserData: GetUserData failed, hr=0x%x", (UINT) hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_BAD_ADDRESS;
|
|
ERROR_OUT(("DCRNCConferenceManager::GetUserData: null pRosterEntry"));
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::GetUserData, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(UINT) DCRNCConferenceManager::
|
|
GetPluggableConnID
|
|
(
|
|
LPCSTR pcszNodeAddress
|
|
)
|
|
{
|
|
return ::GetPluggableTransportConnID(pcszNodeAddress);
|
|
}
|
|
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// Implementation of Methods for DCRNCConferenceManager
|
|
//
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
void DCRNCConferenceManager::
|
|
WndMsgHandler(UINT uMsg, LPARAM lParam)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::WndMsgHandler);
|
|
TRACE_OUT(("DCRNCConferenceManager::WndMsgHandler: uMsg=%u, lParam=0x%x", (UINT) uMsg, (UINT) lParam));
|
|
|
|
switch (uMsg)
|
|
{
|
|
case NCMSG_FIRST_ROSTER_RECVD:
|
|
{
|
|
PCONFERENCE pConf = (PCONFERENCE) lParam;
|
|
if (NULL != pConf)
|
|
{
|
|
pConf->FirstRoster();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NCMSG_QUERY_REMOTE_FAILURE:
|
|
{
|
|
CQueryRemoteWork *pWork = (CQueryRemoteWork *) lParam;
|
|
if (NULL != pWork)
|
|
{
|
|
pWork->SyncQueryRemoteResult();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("DCRNCConferenceManager::WndMsgHandler: unknown msg=%u, lParam=0x%x", uMsg, (UINT) lParam));
|
|
break;
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::WndMsgHandler);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CreateNewConference - create a new instance of DCRNCConference and add */
|
|
/* it to the conference list. */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConferenceManager::
|
|
CreateNewConference
|
|
(
|
|
LPCWSTR pcwszConfName,
|
|
GCCConfID nConfID,
|
|
PCONFERENCE *ppConf,
|
|
BOOL fFindExistingConf,
|
|
BOOL fSecure
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DebugEntry(DCRNCConferenceManager::CreateNewConference);
|
|
ASSERT(ppConf);
|
|
|
|
// Make sure there is not already an active conference of the same name.
|
|
PCONFERENCE pConf = GetConferenceFromName(pcwszConfName);
|
|
if (NULL == pConf)
|
|
{
|
|
// Add new conference
|
|
DBG_SAVE_FILE_LINE
|
|
pConf = new DCRNCConference(pcwszConfName, nConfID, fSecure, &hr);
|
|
if (NULL != pConf && NO_ERROR == hr)
|
|
{
|
|
// Conference added, so include in list.
|
|
m_ConfList.Append(pConf);
|
|
#ifdef _DEBUG
|
|
pConf->OnAppended();
|
|
#endif
|
|
|
|
// This reference is for nmcom.dll so that ReleaseInterface will do
|
|
// the right thing.
|
|
pConf->AddRef();
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConferenceManager::CreateNewConference: can't create conf, hr=0x%x, pConf=0x%p", (UINT) hr, pConf));
|
|
if (pConf == NULL)
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
pConf->Release();
|
|
pConf = NULL;
|
|
}
|
|
}
|
|
|
|
*ppConf = pConf;
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("DCRNCConferenceManager::CreateNewConference: conf already exists"));
|
|
hr = UI_RC_CONFERENCE_ALREADY_EXISTS;
|
|
*ppConf = fFindExistingConf ? pConf : NULL;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::CreateNewConference, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* GetConfIDFromMessage() - Get the conference ID from the message. */
|
|
/***************************************************************************/
|
|
GCCConfID GetConfIDFromMessage ( GCCMessage * pGCCMessage )
|
|
{
|
|
GCCConfID nConfID = pGCCMessage->nConfID;
|
|
|
|
#ifdef _DEBUG
|
|
/************************************************************************/
|
|
/* Dig the conference ID out of the message. */
|
|
/************************************************************************/
|
|
switch (pGCCMessage->message_type)
|
|
{
|
|
case GCC_CREATE_INDICATION:
|
|
// nConfID = pGCCMessage->u.create_indication.conference_id;
|
|
break;
|
|
|
|
case GCC_CREATE_CONFIRM:
|
|
// nConfID = pGCCMessage->u.create_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_JOIN_CONFIRM:
|
|
// nConfID = pGCCMessage->u.join_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_INVITE_CONFIRM:
|
|
// nConfID = pGCCMessage->u.invite_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_ADD_CONFIRM:
|
|
// nConfID = pGCCMessage->u.add_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_DISCONNECT_INDICATION:
|
|
// nConfID = pGCCMessage->u.disconnect_indication.conference_id;
|
|
break;
|
|
|
|
case GCC_DISCONNECT_CONFIRM:
|
|
// nConfID = pGCCMessage->u.disconnect_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_TERMINATE_INDICATION:
|
|
// nConfID = pGCCMessage->u.terminate_indication.conference_id;
|
|
break;
|
|
|
|
case GCC_TERMINATE_CONFIRM:
|
|
// nConfID = pGCCMessage->u.terminate_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_ANNOUNCE_PRESENCE_CONFIRM:
|
|
// nConfID = pGCCMessage->u.announce_presence_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_ROSTER_REPORT_INDICATION:
|
|
// nConfID = pGCCMessage->u.conf_roster_report_indication.conference_id;
|
|
break;
|
|
|
|
case GCC_ROSTER_INQUIRE_CONFIRM:
|
|
// nConfID = pGCCMessage->u.conf_roster_inquire_confirm.conference_id;
|
|
break;
|
|
|
|
case GCC_PERMIT_TO_ANNOUNCE_PRESENCE:
|
|
// nConfID = pGCCMessage->u.permit_to_announce_presence.conference_id;
|
|
break;
|
|
|
|
case GCC_EJECT_USER_INDICATION:
|
|
// nConfID = pGCCMessage->u.eject_user_indication.conference_id;
|
|
break;
|
|
|
|
default :
|
|
// nConfID = 0;
|
|
ERROR_OUT(("Unknown message"));
|
|
break;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
return nConfID;
|
|
}
|
|
|
|
|
|
PCONFERENCE DCRNCConferenceManager::
|
|
GetConferenceFromID ( GCCConfID conferenceID )
|
|
{
|
|
PCONFERENCE pConf = NULL;
|
|
m_ConfList.Reset();
|
|
while (NULL != (pConf = m_ConfList.Iterate()))
|
|
{
|
|
if (pConf->GetID() == conferenceID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return pConf;
|
|
}
|
|
|
|
|
|
PCONFERENCE DCRNCConferenceManager::
|
|
GetConferenceFromName ( LPCWSTR pcwszConfName )
|
|
{
|
|
PCONFERENCE pConf = NULL;
|
|
if (! ::IsEmptyStringW(pcwszConfName))
|
|
{
|
|
m_ConfList.Reset();
|
|
while (NULL != (pConf = m_ConfList.Iterate()))
|
|
{
|
|
if ((0 == ::My_strcmpW(pConf->GetName(), pcwszConfName)) &&
|
|
(pConf->IsActive()))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pConf;
|
|
}
|
|
|
|
|
|
// GetConferenceFromNumber - get the T120 conference with the specified number.
|
|
|
|
PCONFERENCE DCRNCConferenceManager::
|
|
GetConferenceFromNumber ( GCCNumericString NumericName )
|
|
{
|
|
PCONFERENCE pConf = NULL;
|
|
|
|
if (! ::IsEmptyStringA(NumericName))
|
|
{
|
|
m_ConfList.Reset();
|
|
while (NULL != (pConf = m_ConfList.Iterate()))
|
|
{
|
|
LPSTR pszConfNumericName = pConf->GetNumericName();
|
|
if (NULL != pszConfNumericName &&
|
|
0 == ::lstrcmpA(pszConfNumericName, NumericName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pConf;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Handle a GCC callback. */
|
|
/****************************************************************************/
|
|
void DCRNCConferenceManager::
|
|
HandleGCCCallback ( GCCMessage * pGCCMessage )
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::HandleGCCCallback);
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: msg_type=%u", (UINT) pGCCMessage->message_type));
|
|
|
|
switch (pGCCMessage->message_type)
|
|
{
|
|
case GCC_CREATE_CONFIRM:
|
|
{
|
|
PCONFERENCE pConf;
|
|
LPWSTR pwszConfName;
|
|
|
|
// For create confirm, the conference won't
|
|
// know its ID yet (it is contained in this message), so get
|
|
// the conference by name.
|
|
if (NO_ERROR == ::GetUnicodeFromGCC(
|
|
pGCCMessage->u.create_confirm.conference_name.numeric_string,
|
|
pGCCMessage->u.create_confirm.conference_name.text_string,
|
|
&pwszConfName))
|
|
{
|
|
pConf = GetConferenceFromName(pwszConfName);
|
|
if (NULL != pConf)
|
|
{
|
|
pConf->HandleGCCCallback(pGCCMessage);
|
|
}
|
|
delete pwszConfName;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GCC_JOIN_CONFIRM:
|
|
HandleJoinConfirm(&(pGCCMessage->u.join_confirm));
|
|
break;
|
|
|
|
case GCC_CONDUCT_GIVE_INDICATION:
|
|
HandleConductGiveInd(&(pGCCMessage->u.conduct_give_indication));
|
|
break;
|
|
|
|
case GCC_JOIN_INDICATION:
|
|
HandleJoinInd(&(pGCCMessage->u.join_indication));
|
|
break;
|
|
|
|
case GCC_ADD_INDICATION:
|
|
HandleAddInd(&(pGCCMessage->u.add_indication));
|
|
break;
|
|
|
|
case GCC_SUB_INITIALIZED_INDICATION:
|
|
HandleSubInitializedInd(&(pGCCMessage->u.conf_sub_initialized_indication));
|
|
break;
|
|
|
|
case GCC_ROSTER_REPORT_INDICATION:
|
|
// update the (node id, name) list and user data
|
|
UpdateNodeIdNameListAndUserData(pGCCMessage);
|
|
// fall through
|
|
case GCC_INVITE_CONFIRM:
|
|
case GCC_ADD_CONFIRM:
|
|
case GCC_DISCONNECT_INDICATION:
|
|
case GCC_DISCONNECT_CONFIRM:
|
|
case GCC_TERMINATE_INDICATION:
|
|
case GCC_TERMINATE_CONFIRM:
|
|
case GCC_ANNOUNCE_PRESENCE_CONFIRM:
|
|
case GCC_ROSTER_INQUIRE_CONFIRM:
|
|
case GCC_PERMIT_TO_ANNOUNCE_PRESENCE:
|
|
case GCC_EJECT_USER_INDICATION:
|
|
{
|
|
/****************************************************************/
|
|
/* All these events are passed straight onto one of our */
|
|
/* conferences. */
|
|
/****************************************************************/
|
|
|
|
/****************************************************************/
|
|
/* Get the conference ID from the message */
|
|
/****************************************************************/
|
|
GCCConfID nConfID = ::GetConfIDFromMessage(pGCCMessage);
|
|
|
|
/****************************************************************/
|
|
/* See whether we have a conference with this ID; */
|
|
/****************************************************************/
|
|
PCONFERENCE pConf = GetConferenceFromID(nConfID);
|
|
if (NULL != pConf)
|
|
{
|
|
/****************************************************************/
|
|
/* Pass the event onto the conference. */
|
|
/****************************************************************/
|
|
pConf->HandleGCCCallback(pGCCMessage);
|
|
}
|
|
else
|
|
{
|
|
// bugbug: should still reply to indications that require a response.
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: No conference found with ID %d", nConfID));
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef TSTATUS_INDICATION
|
|
case GCC_TRANSPORT_STATUS_INDICATION:
|
|
{
|
|
WORD state = 0;
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: GCC msg type GCC_TRANSPORT_STATUS_INDICATION"));
|
|
TRACE_OUT(("Device identifier '%s'",
|
|
pGCCMessage->u.transport_status.device_identifier));
|
|
TRACE_OUT(("Remote address '%s'",
|
|
pGCCMessage->u.transport_status.remote_address));
|
|
TRACE_OUT(("Message '%s'",
|
|
pGCCMessage->u.transport_status.message));
|
|
state = pGCCMessage->u.transport_status.state;
|
|
#ifdef DEBUG
|
|
LPSTR stateString =
|
|
(state == TSTATE_NOT_READY ? "TSTATE_NOT_READY" :
|
|
(state == TSTATE_NOT_CONNECTED ? "TSTATE_NOT_CONNECTED" :
|
|
(state == TSTATE_CONNECT_PENDING ? "TSTATE_CONNECT_PENDING" :
|
|
(state == TSTATE_CONNECTED ? "TSTATE_CONNECTED" :
|
|
(state == TSTATE_REMOVED ? "TSTATE_REMOVED" :
|
|
("UNKNOWN STATE"))))));
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: Transport state %d (%s)",
|
|
pGCCMessage->u.transport_status.state,
|
|
(const char *)stateString));
|
|
#endif // DEBUG
|
|
}
|
|
break;
|
|
|
|
case GCC_STATUS_INDICATION:
|
|
{
|
|
WORD state = 0;
|
|
#ifdef DEBUG
|
|
LPSTR stateString =
|
|
(state == GCC_STATUS_PACKET_RESOURCE_FAILURE ? "GCC_STATUS_PACKET_RESOURCE_FAILURE " :
|
|
(state == GCC_STATUS_PACKET_LENGTH_EXCEEDED ? "GCC_STATUS_PACKET_LENGTH_EXCEEDED " :
|
|
(state == GCC_STATUS_CTL_SAP_RESOURCE_ERROR ? "GCC_STATUS_CTL_SAP_RESOURCE_ERROR " :
|
|
(state == GCC_STATUS_APP_SAP_RESOURCE_ERROR ? "GCC_STATUS_APP_SAP_RESOURCE_ERROR " :
|
|
(state == GCC_STATUS_CONF_RESOURCE_ERROR ? "GCC_STATUS_CONF_RESOURCE_ERROR " :
|
|
(state == GCC_STATUS_INCOMPATIBLE_PROTOCOL ? "GCC_STATUS_INCOMPATIBLE_PROTOCOL " :
|
|
(state == GCC_STATUS_JOIN_FAILED_BAD_CONF_NAME ? "GCC_STATUS_JOIN_FAILED_BAD_CONF_NAME" :
|
|
(state == GCC_STATUS_JOIN_FAILED_BAD_CONVENER ? "GCC_STATUS_JOIN_FAILED_BAD_CONVENER " :
|
|
(state == GCC_STATUS_JOIN_FAILED_LOCKED ? "GCC_STATUS_JOIN_FAILED_LOCKED " :
|
|
("UNKNOWN STATUS"))))))))));
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: GCC_STATUS_INDICATION, type %d (%s)",
|
|
pGCCMessage->u.status_indication.status_message_type,
|
|
(const char *)stateString));
|
|
#endif // DEBUG
|
|
}
|
|
break;
|
|
#endif // TSTATUS_INDICATION
|
|
|
|
case GCC_INVITE_INDICATION:
|
|
/****************************************************************/
|
|
/* We have been invited into a conference: Create a new */
|
|
/* (incoming) conference. */
|
|
/****************************************************************/
|
|
HandleInviteIndication(&(pGCCMessage->u.invite_indication));
|
|
break;
|
|
|
|
case GCC_CREATE_INDICATION:
|
|
/****************************************************************/
|
|
/* A new conference has been created. */
|
|
/****************************************************************/
|
|
HandleCreateIndication(&(pGCCMessage->u.create_indication));
|
|
break;
|
|
|
|
case GCC_QUERY_CONFIRM:
|
|
HandleQueryConfirmation(&(pGCCMessage->u.query_confirm));
|
|
break;
|
|
|
|
case GCC_QUERY_INDICATION:
|
|
HandleQueryIndication(&(pGCCMessage->u.query_indication));
|
|
break;
|
|
|
|
case GCC_CONNECTION_BROKEN_INDICATION:
|
|
BroadcastGCCCallback(pGCCMessage);
|
|
break;
|
|
|
|
case GCC_LOCK_INDICATION:
|
|
HandleLockIndication(&(pGCCMessage->u.lock_indication));
|
|
break;
|
|
|
|
// case GCC_APPLICATION_INVOKE_CONFIRM:
|
|
// This just indicates the g_pIT120ControlSap->AppletInvokeRequest succeeded.
|
|
// There is no official confirmation from the remote machine.
|
|
// FUTURE: Add protocol + code to respond to the launch request.
|
|
// break;
|
|
|
|
case GCC_APPLICATION_INVOKE_INDICATION:
|
|
HandleApplicationInvokeIndication(&(pGCCMessage->u.application_invoke_indication));
|
|
break;
|
|
|
|
case GCC_UNLOCK_INDICATION:
|
|
HandleUnlockIndication(&(pGCCMessage->u.unlock_indication));
|
|
break;
|
|
|
|
case GCC_TIME_INQUIRE_INDICATION:
|
|
HandleTimeInquireIndication(&(pGCCMessage->u.time_inquire_indication));
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
case GCC_APP_ROSTER_REPORT_INDICATION:
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: GCC msg type GCC_APP_ROSTER_REPORT_INDICATION"));
|
|
break;
|
|
#endif /* DEBUG */
|
|
|
|
default :
|
|
/****************************************************************/
|
|
/* This should be an exhaustive list of all the events we dont */
|
|
/* handle: */
|
|
/* */
|
|
/* GCC_TEXT_MESSAGE_INDICATION */
|
|
/* GCC_TIME_REMAINING_INDICATION */
|
|
/* */
|
|
/* GCC_ALLOCATE_HANDLE_CONFIRM */
|
|
/* GCC_APP_ROSTER_INQUIRE_CONFIRM */
|
|
/* GCC_ASSIGN_TOKEN_CONFIRM */
|
|
/* GCC_ASSISTANCE_CONFIRM */
|
|
/* GCC_ASSISTANCE_INDICATION */
|
|
/* GCC_CONDUCT_ASK_CONFIRM */
|
|
/* GCC_CONDUCT_ASK_INDICATION */
|
|
/* GCC_CONDUCT_ASSIGN_CONFIRM */
|
|
/* GCC_CONDUCT_ASSIGN_INDICATION */
|
|
/* GCC_CONDUCT_GIVE_CONFIRM */
|
|
/* GCC_CONDUCT_GRANT_CONFIRM */
|
|
/* GCC_CONDUCT_GRANT_INDICATION */
|
|
/* GCC_CONDUCT_INQUIRE_CONFIRM */
|
|
/* GCC_CONDUCT_PLEASE_CONFIRM */
|
|
/* GCC_CONDUCT_PLEASE_INDICATION */
|
|
/* GCC_CONDUCT_RELEASE_CONFIRM */
|
|
/* GCC_CONDUCT_RELEASE_INDICATION */
|
|
/* GCC_CONFERENCE_EXTEND_CONFIRM */
|
|
/* GCC_CONFERENCE_EXTEND_INDICATION */
|
|
/* GCC_DELETE_ENTRY_CONFIRM */
|
|
/* GCC_EJECT_USER_CONFIRM */
|
|
/* GCC_ENROLL_CONFIRM */
|
|
/* GCC_LOCK_CONFIRM */
|
|
/* GCC_LOCK_REPORT_INDICATION */
|
|
/* GCC_MONITOR_CONFIRM */
|
|
/* GCC_MONITOR_INDICATION */
|
|
/* GCC_PERMIT_TO_ENROLL_INDICATION: */
|
|
/* GCC_REGISTER_CHANNEL_CONFIRM */
|
|
/* GCC_RETRIEVE_ENTRY_CONFIRM */
|
|
/* GCC_SET_PARAMETER_CONFIRM */
|
|
/* GCC_TEXT_MESSAGE_CONFIRM */
|
|
/* GCC_TIME_INQUIRE_CONFIRM */
|
|
/* GCC_TIME_REMAINING_CONFIRM */
|
|
/* GCC_TRANSFER_CONFIRM */
|
|
/* GCC_TRANSFER_INDICATION */
|
|
/* GCC_UNLOCK_CONFIRM */
|
|
/****************************************************************/
|
|
TRACE_OUT(("DCRNCConferenceManager::HandleGCCCallback: Ignoring msg_type=%u", pGCCMessage->message_type));
|
|
break;
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::HandleGCCCallback);
|
|
}
|
|
|
|
|
|
void DCRNCConferenceManager::
|
|
BroadcastGCCCallback ( GCCMessage *pGCCMessage )
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::BroadcastGCCCallback);
|
|
|
|
// An event has come in that is of potential interest to all
|
|
// conferences, so pass it on to them.
|
|
// Note that this is currently only used for broken logical
|
|
// connections that are actually on a single conference because
|
|
// T120 maps logical connections to conferences.
|
|
PCONFERENCE pConf;
|
|
m_ConfList.Reset();
|
|
while (NULL != (pConf = m_ConfList.Iterate()))
|
|
{
|
|
pConf->HandleGCCCallback(pGCCMessage);
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::BroadcastGCCCallback);
|
|
}
|
|
|
|
|
|
// HandleJoinConfirm - handle a GCC_JOIN_CONFIRM message.
|
|
void DCRNCConferenceManager::
|
|
HandleJoinConfirm ( JoinConfirmMessage * pJoinConfirm )
|
|
{
|
|
PCONFERENCE pConf = NULL;
|
|
LPWSTR pwszConfName;
|
|
|
|
DebugEntry(DCRNCConferenceManager::HandleJoinConfirm);
|
|
|
|
// For join confirm, the conference won't know its ID yet
|
|
// (it is contained in this message),
|
|
// so get the conference by name.
|
|
HRESULT hr = GetUnicodeFromGCC((PCSTR)pJoinConfirm->conference_name.numeric_string,
|
|
pJoinConfirm->conference_name.text_string,
|
|
&pwszConfName);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
pConf = GetConferenceFromName(pwszConfName);
|
|
delete pwszConfName;
|
|
}
|
|
|
|
if (pConf == NULL)
|
|
{
|
|
pConf = GetConferenceFromNumber(pJoinConfirm->conference_name.numeric_string);
|
|
}
|
|
|
|
if (pConf != NULL)
|
|
{
|
|
pConf->HandleJoinConfirm(pJoinConfirm);
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::HandleJoinConfirm);
|
|
}
|
|
|
|
|
|
#ifdef ENABLE_START_REMOTE
|
|
// HandleCreateIndication - handle a GCC_CREATE_INDICATION message.
|
|
void DCRNCConferenceManager::
|
|
HandleCreateIndication ( CreateIndicationMessage * pCreateMessage )
|
|
{
|
|
PCONFERENCE pNewConference = NULL;
|
|
HRESULT hr = UI_RC_USER_REJECTED;
|
|
LPWSTR name;
|
|
|
|
DebugEntry(DCRNCConferenceManager::HandleCreateIndication);
|
|
|
|
TRACE_OUT(("GCC event: GCC_CREATE_INDICATION"));
|
|
TRACE_OUT(("Conference ID %ld", pCreateMessage->conference_id));
|
|
if (pCreateMessage->conductor_privilege_list == NULL)
|
|
{
|
|
TRACE_OUT(("Conductor privilege list is NULL"));
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("Conductor priv, terminate allowed %d",
|
|
pCreateMessage->conductor_privilege_list->terminate_is_allowed));
|
|
}
|
|
|
|
if (pCreateMessage->conducted_mode_privilege_list == NULL)
|
|
{
|
|
TRACE_OUT(("Conducted mode privilege list is NULL"));
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("Conducted mode priv, terminate allowed %d",
|
|
pCreateMessage->conducted_mode_privilege_list->terminate_is_allowed));
|
|
}
|
|
|
|
if (pCreateMessage->non_conducted_privilege_list == NULL)
|
|
{
|
|
TRACE_OUT(("Non-conducted mode privilege list is NULL"));
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("non-conducted priv, terminate allowed %d",
|
|
pCreateMessage->non_conducted_privilege_list->terminate_is_allowed));
|
|
}
|
|
|
|
hr = ::GetUnicodeFromGCC((PCSTR)pCreateMessage->conference_name.numeric_string,
|
|
(PWSTR)pCreateMessage->conference_name.text_string,
|
|
&name);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
hr = CreateNewConference(name,
|
|
pCreateMessage->conference_id,
|
|
&pNewConference);
|
|
delete name;
|
|
}
|
|
|
|
if (NO_ERROR == hr)
|
|
{
|
|
hr = pNewConference->StartIncoming();
|
|
if (NO_ERROR == hr)
|
|
{
|
|
g_pNCConfMgr->CreateConferenceRequest(pNewConference);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ERROR_OUT(("Failed to create incoming conference"));
|
|
GCCCreateResponse(hr, pMsg->conference_id, &pMsg->conference_name);
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::HandleCreateIndication);
|
|
}
|
|
#endif // ENABLE_START_REMOTE
|
|
|
|
|
|
void DCRNCConferenceManager::
|
|
GCCCreateResponse
|
|
(
|
|
HRESULT hr,
|
|
GCCConfID conference_id,
|
|
GCCConferenceName * pGCCName
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::GCCCreateResponse);
|
|
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfCreateResponse(
|
|
NULL,
|
|
conference_id,
|
|
0,
|
|
NULL, /* domain_parameters */
|
|
0, /* number_of_network_addresses */
|
|
NULL, /* local_network_address_list */
|
|
0, /* number_of_user_data_members */
|
|
NULL, /* user_data_list */
|
|
::MapRCToGCCResult(hr));
|
|
TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfCreateResponse, rc=%d", GCCrc));
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::GCCCreateResponse);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* HandleInviteIndication - handle a GCC_INVITE_INDICATION message. */
|
|
/****************************************************************************/
|
|
void DCRNCConferenceManager::
|
|
HandleInviteIndication ( InviteIndicationMessage * pInviteMessage )
|
|
{
|
|
LPWSTR pwszConfName;
|
|
PCONFERENCE pNewConference = NULL;
|
|
HRESULT hr;
|
|
CLogicalConnection *pConEntry;
|
|
CInviteIndWork *pInviteUI;
|
|
PT120PRODUCTVERSION pVersion;
|
|
|
|
DebugEntry(DCRNCConferenceManager::HandleInviteIndication);
|
|
|
|
TRACE_OUT(("GCC event: GCC_INVITE_INDICATION"));
|
|
TRACE_OUT(("Invited into conference ID %ld", pInviteMessage->conference_id));
|
|
|
|
|
|
// Create a new conference, using the constructor for an incoming T120
|
|
// conference.
|
|
hr = GetUnicodeFromGCC((PCSTR)pInviteMessage->conference_name.numeric_string,
|
|
(PWSTR)pInviteMessage->conference_name.text_string,
|
|
&pwszConfName);
|
|
|
|
//
|
|
// Check to see if we're allowed to be invited. We may never get here
|
|
// if we properly signal callers that we won't accept a nonsecure
|
|
// Invite, but if they do it anyway or lead with T.120 we will enforce
|
|
// the registry setting here.
|
|
//
|
|
RegEntry re(CONFERENCING_KEY, HKEY_CURRENT_USER);
|
|
|
|
if ( re.GetNumber(REGVAL_SECURITY_INCOMING_REQUIRED,
|
|
DEFAULT_SECURITY_INCOMING_REQUIRED ))
|
|
{
|
|
if ( !pInviteMessage->fSecure )
|
|
{
|
|
WARNING_OUT(("HandleInviteIndication: CONNECTION is NOT SECURE"));
|
|
hr = UI_RC_T120_SECURITY_FAILED;
|
|
}
|
|
}
|
|
|
|
if (NO_ERROR == hr)
|
|
{
|
|
hr = CreateNewConference(pwszConfName,
|
|
pInviteMessage->conference_id,
|
|
&pNewConference,
|
|
FALSE,
|
|
pInviteMessage->fSecure);
|
|
delete pwszConfName;
|
|
if (NO_ERROR == hr)
|
|
{
|
|
// Make sure the conference object does not go away randomly.
|
|
pNewConference->AddRef();
|
|
|
|
pNewConference->SetActive(FALSE);
|
|
DBG_SAVE_FILE_LINE
|
|
pConEntry = pNewConference->NewLogicalConnection(CONF_CON_INVITED,
|
|
pInviteMessage->connection_handle,
|
|
NULL,
|
|
0,
|
|
pInviteMessage->fSecure);
|
|
if (NULL != pConEntry)
|
|
{
|
|
// Save the T120 connection handle in the connection record
|
|
// so that disconnect indications take down the conference.
|
|
pConEntry->SetInviteReqConnHandle(pInviteMessage->connection_handle);
|
|
hr = pNewConference->StartIncoming();
|
|
|
|
// Linearize the invite requests so that two invites don't fight each other
|
|
// for attention, and so that the second invite has a conference to see in
|
|
// rosters and join if the first invite gets accepted.
|
|
if (NO_ERROR == hr)
|
|
{
|
|
pVersion = ::GetVersionData(pInviteMessage->number_of_user_data_members,
|
|
pInviteMessage->user_data_list);
|
|
DBG_SAVE_FILE_LINE
|
|
pInviteUI = new CInviteIndWork(pNewConference,
|
|
(LPCWSTR)(pInviteMessage->caller_identifier),
|
|
pVersion,
|
|
pInviteMessage->user_data_list,
|
|
pInviteMessage->number_of_user_data_members,
|
|
pConEntry);
|
|
if (pInviteUI)
|
|
{
|
|
pNewConference->SetInviteIndWork(pInviteUI);
|
|
m_InviteIndWorkList.AddWorkItem(pInviteUI);
|
|
hr = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// This Release corresponds to the above AddRef.
|
|
if (0 == pNewConference->Release())
|
|
{
|
|
// Make sure no one will use it any more.
|
|
pNewConference = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NO_ERROR != hr)
|
|
{
|
|
if (NULL != pNewConference)
|
|
{
|
|
pNewConference->InviteResponse(hr);
|
|
}
|
|
else
|
|
{
|
|
// LONCHANC: we have to somehow send a response PDU out.
|
|
g_pIT120ControlSap->ConfInviteResponse(
|
|
pInviteMessage->conference_id,
|
|
NULL,
|
|
pInviteMessage->fSecure,
|
|
NULL, // domain parms
|
|
0, // number_of_network_addresses
|
|
NULL, // local_network_address_list
|
|
g_nVersionRecords, // number_of_user_data_members
|
|
g_ppVersionUserData,// user_data_list
|
|
GCC_RESULT_ENTRY_ALREADY_EXISTS);
|
|
}
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::HandleInviteIndication, hr);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* HandleJoinInd - handle a GCC_JOIN_INDICATION message. */
|
|
/****************************************************************************/
|
|
void DCRNCConferenceManager::
|
|
HandleJoinInd ( JoinIndicationMessage * pJoinInd )
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::HandleJoinInd);
|
|
|
|
GCCResult Result = GCC_RESULT_SUCCESSFUL;
|
|
|
|
// Look up conference ID, and if not found, dismiss request.
|
|
CJoinIndWork *pJoinUI;
|
|
CLogicalConnection *pConEntry;
|
|
PT120PRODUCTVERSION pVersion;
|
|
|
|
PCONFERENCE pConf = GetConferenceFromID(pJoinInd->conference_id);
|
|
if (NULL != pConf)
|
|
{
|
|
//
|
|
// Under RDS, if this conference has been hit with bad passwords
|
|
// too many times, everyone is out of luck and we will not accept
|
|
// anyone into this conference anymore.
|
|
//
|
|
|
|
if (g_bRDS && ( pConf->InvalidPwdCount() >= MAX_INVALID_PASSWORDS ))
|
|
{
|
|
WARNING_OUT(("RDS: locked out by too many bad pwd attempts"));
|
|
Result = GCC_RESULT_USER_REJECTED;
|
|
}
|
|
// Validate conference password, if required.
|
|
else if (!pConf->ValidatePassword(pJoinInd->password_challenge))
|
|
{
|
|
//
|
|
// Only increment the wrong password count if one was
|
|
// supplied
|
|
//
|
|
|
|
if ( pJoinInd->password_challenge )
|
|
pConf->IncInvalidPwdCount();
|
|
|
|
if ( g_bRDS &&
|
|
( pConf->InvalidPwdCount() >= MAX_INVALID_PASSWORDS ))
|
|
{
|
|
Result = GCC_RESULT_USER_REJECTED;
|
|
}
|
|
else
|
|
{
|
|
Result = GCC_RESULT_INVALID_PASSWORD;
|
|
}
|
|
}
|
|
else
|
|
pConf->ResetInvalidPwdCount();
|
|
}
|
|
else
|
|
{
|
|
Result = GCC_RESULT_INVALID_CONFERENCE;
|
|
}
|
|
|
|
if (Result == GCC_RESULT_SUCCESSFUL)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pConEntry = pConf->NewLogicalConnection(
|
|
CONF_CON_JOINED,
|
|
pJoinInd->connection_handle,
|
|
NULL,
|
|
0,
|
|
pConf->IsSecure());
|
|
if (NULL != pConEntry)
|
|
{
|
|
HRESULT hr;
|
|
pVersion = ::GetVersionData(pJoinInd->number_of_user_data_members,
|
|
pJoinInd->user_data_list);
|
|
DBG_SAVE_FILE_LINE
|
|
pJoinUI = new CJoinIndWork(pJoinInd->join_response_tag,
|
|
pConf,
|
|
pJoinInd->caller_identifier,
|
|
pConEntry,
|
|
pVersion,
|
|
pJoinInd->number_of_user_data_members,
|
|
pJoinInd->user_data_list,
|
|
&hr);
|
|
if (NULL != pJoinUI && NO_ERROR == hr)
|
|
{
|
|
m_JoinIndWorkList.AddWorkItem(pJoinUI);
|
|
return;
|
|
}
|
|
|
|
// Handle failure
|
|
delete pJoinUI;
|
|
pConEntry->Delete(UI_RC_OUT_OF_MEMORY);
|
|
}
|
|
Result = GCC_RESULT_RESOURCES_UNAVAILABLE;
|
|
}
|
|
|
|
::GCCJoinResponseWrapper(pJoinInd->join_response_tag,
|
|
NULL,
|
|
Result,
|
|
pJoinInd->conference_id);
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::HandleJoinInd);
|
|
}
|
|
|
|
|
|
void HandleQueryConfirmation ( QueryConfirmMessage * pQueryMessage )
|
|
{
|
|
DebugEntry(HandleQueryConfirmation);
|
|
|
|
ASSERT(g_pQueryRemoteList);
|
|
|
|
CQueryRemoteWork *pQueryRemote;
|
|
|
|
// Must have a pending query and it must be first in
|
|
// sequential work list.
|
|
g_pQueryRemoteList->Reset();
|
|
while (NULL != (pQueryRemote = g_pQueryRemoteList->Iterate()))
|
|
{
|
|
if (pQueryRemote->GetConnectionHandle() == pQueryMessage->connection_handle)
|
|
{
|
|
// GCC has given us a valid query response, so handle it.
|
|
pQueryRemote->HandleQueryConfirmation(pQueryMessage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NULL == pQueryRemote)
|
|
{
|
|
// Unexpected GCC Query Confirmation.
|
|
WARNING_OUT(("HandleQueryConfirmation: Unmatched GCCQueryConfirm"));
|
|
}
|
|
|
|
DebugExitVOID(HandleQueryConfirmation);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* NotifyConferenceComplete() - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
void DCRNCConferenceManager::
|
|
NotifyConferenceComplete
|
|
(
|
|
PCONFERENCE pConf,
|
|
BOOL bIncoming,
|
|
HRESULT result
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::NotifyConferenceComplete);
|
|
|
|
ASSERT(NULL != pConf);
|
|
|
|
// If the new conference was successfully added, then ensure that it
|
|
// is marked as active. This is for the invite case, and is done before
|
|
// telling the UI about the conference.
|
|
HRESULT hr = result;
|
|
if (NO_ERROR == hr)
|
|
{
|
|
pConf->SetActive(TRUE);
|
|
}
|
|
|
|
// If the conference failed to start, tell the UI so that
|
|
// it can display a pop-up.
|
|
// Note this this allows message pre-emption which can cause GCC to give back a GCC event.
|
|
// In particular, a JoinRequest completion event, which must be ignored.
|
|
|
|
// The following is a guard because NotifyConferenceComplete is called all
|
|
// over the place and we do not want the user notified through callbacks
|
|
// for inline errors. All inline errors are meant to trickle back through the
|
|
// originating API, so these callbacks are only enabled once the user is returned
|
|
// success.
|
|
if (pConf->GetNotifyToDo())
|
|
{
|
|
pConf->SetNotifyToDo(FALSE);
|
|
|
|
//
|
|
// LONCHANC: This function may be called inside
|
|
// ConfMgr::ReleaseInterface(). As a result, the global pointer
|
|
// to the callback interface may already be nulled out.
|
|
// Check it before use it.
|
|
//
|
|
if (NULL != g_pCallbackInterface)
|
|
{
|
|
g_pCallbackInterface->OnConferenceStarted(pConf, hr);
|
|
}
|
|
}
|
|
|
|
if (NO_ERROR == hr)
|
|
{
|
|
// If the conference is new as the result of an invite, then it has an entry
|
|
// at the start of the sequential work item list. Now that the conference is up
|
|
// and the UI has been told, this entry is removed to allow other invite
|
|
// requests to be processed.
|
|
m_InviteIndWorkList.RemoveWorkItem(pConf->GetInviteIndWork());
|
|
pConf->SetInviteIndWork(NULL);
|
|
}
|
|
else
|
|
{
|
|
RemoveConference(pConf);
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::NotifyConferenceComplete);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* NotifyRosterChanged() - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
|
|
|
|
// RemoveConference() - remove the conference from the conference list,
|
|
// and destroy the conference.
|
|
void DCRNCConferenceManager::
|
|
RemoveConference ( PCONFERENCE pConf, BOOL fDontCheckList, BOOL fReleaseNow )
|
|
{
|
|
DebugEntry(DCRNCConferenceManager::RemoveConference);
|
|
|
|
if (pConf != NULL)
|
|
{
|
|
if (m_ConfList.Remove(pConf) || fDontCheckList)
|
|
{
|
|
pConf->OnRemoved(fReleaseNow);
|
|
m_InviteIndWorkList.PurgeListEntriesByOwner(pConf);
|
|
m_JoinIndWorkList.PurgeListEntriesByOwner(pConf);
|
|
}
|
|
else
|
|
{
|
|
// If we get here, we haven't found the conference.
|
|
// This actually happens because when a conference is being
|
|
// terminated, its destructor calls DCRNCConference::Leave()
|
|
// to ensure a speedy exit, if required. However, if the
|
|
// conference is currently not yet active (e.g. waiting for
|
|
// the user to supply a password), calling Leave() causes
|
|
// RemoveConference() to be called back. In this case,
|
|
// because the conference has already been removed from the
|
|
// list, this function does nothing.
|
|
}
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConferenceManager::RemoveConference);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* EjectUserFromConference() - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
|
|
|
|
/****************************************************************************/
|
|
/* SendUserTextMessage() - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
|
|
|
|
/****************************************************************************/
|
|
/* TimeRemainingInConference() - see ernccm.hpp */
|
|
/****************************************************************************/
|
|
|
|
|
|
/****************************************************************************/
|
|
/* GCC callback function. */
|
|
/****************************************************************************/
|
|
void CALLBACK DCRNCConferenceManager::
|
|
GCCCallBackHandler ( GCCMessage * pGCCMessage )
|
|
{
|
|
DCRNCConferenceManager *pConfManager;
|
|
|
|
/************************************************************************/
|
|
/* The message has a user defined field which we use to store a pointer */
|
|
/* to the CM class. Use it to pass the message onto CM. */
|
|
/************************************************************************/
|
|
pConfManager = (DCRNCConferenceManager *) pGCCMessage->user_defined;
|
|
|
|
//
|
|
// Check the pointer isnt completely daft,
|
|
// and guard against getting events after shutting down
|
|
// (a current bug in GCC/MCS).
|
|
if (pConfManager == g_pNCConfMgr)
|
|
{
|
|
/************************************************************************/
|
|
/* Pass the message onto CM and return the returned code. */
|
|
/************************************************************************/
|
|
g_pNCConfMgr->HandleGCCCallback(pGCCMessage);
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("Dud user_defined field, pConfMgr=%p, g_pNCConfMgr=%p",
|
|
pConfManager, g_pNCConfMgr));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT GCCJoinResponseWrapper
|
|
(
|
|
GCCResponseTag join_response_tag,
|
|
GCCChallengeRequestResponse *password_challenge,
|
|
GCCResult result,
|
|
GCCConferenceID conferenceID,
|
|
UINT nUserData,
|
|
GCCUserData **ppUserData
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
GCCError GCCrc;
|
|
|
|
DebugEntry(GCCJoinResponseWrapper);
|
|
|
|
TRACE_OUT(("GCC event: GCC_JOIN_INDICATION"));
|
|
TRACE_OUT(("Response tag %d", join_response_tag));
|
|
|
|
if (g_pControlSap->IsThisNodeTopProvider(conferenceID) == FALSE)
|
|
{
|
|
GCCrc = g_pIT120ControlSap->ConfJoinResponse(join_response_tag,
|
|
password_challenge,
|
|
nUserData,
|
|
ppUserData,
|
|
result);
|
|
|
|
}
|
|
else
|
|
{
|
|
GCCrc = g_pIT120ControlSap->ConfJoinResponse(join_response_tag,
|
|
password_challenge,
|
|
g_nVersionRecords,
|
|
g_ppVersionUserData,
|
|
result);
|
|
}
|
|
hr = ::GetGCCRCDetails(GCCrc);
|
|
TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfJoinResponse, rc=%d", GCCrc));
|
|
|
|
if ((GCCrc != GCC_NO_ERROR) &&
|
|
(result != GCC_RESULT_USER_REJECTED))
|
|
{
|
|
/********************************************************************/
|
|
/* If the call to join response fails, we must try again to reject */
|
|
/* the join request. */
|
|
/********************************************************************/
|
|
ERROR_OUT(("GCCJoinResponseWrapper: GCC error %d responding to join ind", GCCrc));
|
|
GCCrc = g_pIT120ControlSap->ConfJoinResponse(join_response_tag,
|
|
password_challenge,
|
|
g_nVersionRecords,
|
|
g_ppVersionUserData,
|
|
GCC_RESULT_USER_REJECTED);
|
|
|
|
TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfJoinResponse (again), rc=%d", GCCrc));
|
|
if (GCCrc != GCC_NO_ERROR)
|
|
{
|
|
/****************************************************************/
|
|
/* If it fails a second time we really are in deep doggy-do. */
|
|
/****************************************************************/
|
|
ERROR_OUT(("GCCJoinResponseWrapper: g_pIT120ControlSap->ConfJoinResponse failed again..."));
|
|
}
|
|
}
|
|
|
|
DebugExitHRESULT(GCCJoinResponseWrapper, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
void HandleQueryIndication ( QueryIndicationMessage * pQueryMessage )
|
|
{
|
|
DebugEntry(HandleQueryIndication);
|
|
|
|
GCCAsymmetryIndicator ai, ai2;
|
|
GCCNodeType node_type;
|
|
GCCError GCCrc;
|
|
CQueryRemoteWork *pQueryRemote = NULL;
|
|
GCCResult result = GCC_RESULT_SUCCESSFUL;
|
|
OSVERSIONINFO osvi;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
if (FALSE == ::GetVersionEx (&osvi))
|
|
{
|
|
ERROR_OUT(("GetVersionEx() failed!"));
|
|
}
|
|
|
|
if ( VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && g_bRDS )
|
|
{
|
|
SOCKET socket_number;
|
|
if (g_pMCSController->FindSocketNumber(pQueryMessage->connection_handle, &socket_number))
|
|
{
|
|
TransportConnection XprtConn;
|
|
SET_SOCKET_CONNECTION(XprtConn, socket_number);
|
|
PSocket pSocket = g_pSocketList->FindByTransportConnection(XprtConn);
|
|
ASSERT(NULL != pSocket);
|
|
if (NULL != pSocket)
|
|
{
|
|
AddToMessageLog(EVENTLOG_INFORMATION_TYPE,
|
|
0,
|
|
MSG_INF_ACCESS,
|
|
pSocket->Remote_Address);
|
|
pSocket->Release();
|
|
}
|
|
}
|
|
}
|
|
// If the caller did not pass in the protocol for deciding who is caller
|
|
// then fabricate something for him and make him the caller.
|
|
|
|
if (pQueryMessage->asymmetry_indicator)
|
|
{
|
|
ai = *pQueryMessage->asymmetry_indicator;
|
|
}
|
|
else
|
|
{
|
|
ai.asymmetry_type = GCC_ASYMMETRY_CALLER;
|
|
ai.random_number = 0;
|
|
}
|
|
|
|
// let's set default random number, which will be read only in the "unknown" case.
|
|
ai2.random_number = ai.random_number;
|
|
|
|
// prepare the query respone
|
|
switch (ai.asymmetry_type)
|
|
{
|
|
case GCC_ASYMMETRY_CALLED:
|
|
ai2.asymmetry_type = GCC_ASYMMETRY_CALLER;
|
|
break;
|
|
case GCC_ASYMMETRY_CALLER:
|
|
ai2.asymmetry_type = GCC_ASYMMETRY_CALLED;
|
|
break;
|
|
case GCC_ASYMMETRY_UNKNOWN:
|
|
// Check if we are not in a pending query
|
|
ASSERT(g_pQueryRemoteList);
|
|
if (! g_pQueryRemoteList->IsEmpty())
|
|
{
|
|
pQueryRemote = g_pQueryRemoteList->PeekHead();
|
|
}
|
|
// If we queryed as unknown
|
|
if (pQueryRemote && pQueryRemote->IsInUnknownQueryRequest())
|
|
{
|
|
pQueryRemote->GetAsymIndicator(&ai2);
|
|
if (ai2.asymmetry_type == GCC_ASYMMETRY_UNKNOWN &&
|
|
ai2.random_number > ai.random_number)
|
|
{
|
|
result = GCC_RESULT_USER_REJECTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ai2.asymmetry_type = GCC_ASYMMETRY_UNKNOWN;
|
|
// ai2.random_number = ~ ai.random_number;
|
|
ai2.random_number--; // lonchanc: we should always be the callee in this case.
|
|
}
|
|
break;
|
|
|
|
default:
|
|
result = GCC_RESULT_USER_REJECTED;
|
|
break;
|
|
}
|
|
|
|
// Figure out my node type.
|
|
LoadAnnouncePresenceParameters(&node_type, NULL, NULL, NULL);
|
|
|
|
// Issue reply.
|
|
GCCrc = g_pIT120ControlSap->ConfQueryResponse(
|
|
pQueryMessage->query_response_tag,
|
|
node_type,
|
|
&ai2,
|
|
g_nVersionRecords,
|
|
g_ppVersionUserData,
|
|
result);
|
|
if (GCCrc)
|
|
{
|
|
TRACE_OUT(("HandleQueryIndication: g_pIT120ControlSap->ConfQueryResponse failed, rc=%d", GCCrc));
|
|
}
|
|
|
|
DebugExitVOID(HandleQueryIndication);
|
|
}
|
|
|
|
|
|
void HandleConductGiveInd ( ConductGiveIndicationMessage * pConductGiveInd )
|
|
{
|
|
DebugEntry(HandleConductGiveInd);
|
|
|
|
// Node controller does not accept conductorship being handed over
|
|
// from another node, so reject request.
|
|
GCCError GCCrc = g_pIT120ControlSap->ConductorGiveResponse(pConductGiveInd->conference_id,
|
|
GCC_RESULT_USER_REJECTED);
|
|
TRACE_OUT(("HandleConductGiveInd: Failed to reject ConductGiveIndication, gcc_rc=%u", (UINT) GCCrc));
|
|
|
|
DebugExitVOID(HandleConductGiveInd);
|
|
}
|
|
|
|
|
|
void HandleAddInd ( AddIndicationMessage * pAddInd )
|
|
{
|
|
DebugEntry(HandleAddInd);
|
|
|
|
// Just reject the request because we don't do adds on behalf of someone else.
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfAddResponse(
|
|
pAddInd->add_response_tag, // add_response_tag
|
|
pAddInd->conference_id, // conference_id
|
|
pAddInd->requesting_node_id, // requesting_node
|
|
0, // number_of_user_data_members
|
|
NULL, // user_data_list
|
|
GCC_RESULT_USER_REJECTED); // result
|
|
TRACE_OUT(("HandleAddInd: Failed to reject AddIndication, gcc_rc=%u", (UINT) GCCrc));
|
|
|
|
DebugExitVOID(HandleAddInd);
|
|
}
|
|
|
|
|
|
void HandleLockIndication ( LockIndicationMessage * pLockInd )
|
|
{
|
|
DebugEntry(HandleLockIndication);
|
|
|
|
// Just reject the request because we don't do locked conferences.
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfLockResponse(
|
|
pLockInd->conference_id, // conference_id
|
|
pLockInd->requesting_node_id, // requesting_node
|
|
GCC_RESULT_USER_REJECTED); // result
|
|
TRACE_OUT(("HandleLockIndication: Failed to reject LockIndication, gcc_rc=%u", (UINT) GCCrc));
|
|
|
|
DebugExitVOID(HandleLockIndication);
|
|
}
|
|
|
|
|
|
void HandleUnlockIndication ( UnlockIndicationMessage * pUnlockInd )
|
|
{
|
|
DebugEntry(HandleUnlockIndication);
|
|
|
|
// Reject the request because we don't manage
|
|
// locking/unlocking of conferences.
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfLockResponse(
|
|
pUnlockInd->conference_id, // conference_id
|
|
pUnlockInd->requesting_node_id, // requesting_node
|
|
GCC_RESULT_USER_REJECTED); // result
|
|
TRACE_OUT(("HandleUnlockIndication: Failed to reject UnlockIndication, gcc_rc=%u", (UINT) GCCrc));
|
|
|
|
DebugExitVOID(HandleUnlockIndication);
|
|
}
|
|
|
|
|
|
void HandleSubInitializedInd ( SubInitializedIndicationMessage * pSubInitInd )
|
|
{
|
|
DebugEntry(HandleSubInitializedInd);
|
|
|
|
CLogicalConnection *pConEntry = g_pNCConfMgr->GetConEntryFromConnectionHandle(
|
|
pSubInitInd->connection_handle);
|
|
if (NULL != pConEntry)
|
|
{
|
|
pConEntry->SetConnectionNodeID(pSubInitInd->subordinate_node_id);
|
|
}
|
|
|
|
DebugExitVOID(HandleSubInitializedInd);
|
|
}
|
|
|
|
|
|
// This function is used by the GCC_SUB_INITIALIZED_INDICATION handler.
|
|
// This handler was added to bind the request to enter someone into
|
|
// a conference to the resulting conference roster, so that you could
|
|
// tell which new entry in the roster was the one you requested in.
|
|
// Since the above handler only gets a connection handle (recast here to a
|
|
// request handle) and a userID, this means that the local GCC implementation
|
|
// is guarunteeing that connection handles are unique to a local machine
|
|
// and not duplicated in different conferences (this fact is also being used
|
|
// by the node controller to know when someone invited into a conference leaves).
|
|
CLogicalConnection * DCRNCConferenceManager::
|
|
GetConEntryFromConnectionHandle ( ConnectionHandle hInviteIndConn )
|
|
{
|
|
PCONFERENCE pConf;
|
|
CLogicalConnection *pConEntry;
|
|
|
|
m_ConfList.Reset();
|
|
while (NULL != (pConf = m_ConfList.Iterate()))
|
|
{
|
|
pConEntry = pConf->GetConEntry(hInviteIndConn);
|
|
if (NULL != pConEntry)
|
|
{
|
|
return(pConEntry);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
void HandleTimeInquireIndication ( TimeInquireIndicationMessage * pTimeInquireInd )
|
|
{
|
|
DebugEntry(HandleTimeInquireIndication);
|
|
|
|
// Since we don't currently time messages, and there is no mechanism to say this,
|
|
// or to even say that there is no such conference that we know about, just
|
|
// say that the conference has one hour remaining, with the same scope as the request.
|
|
UserID node_id = pTimeInquireInd->time_is_conference_wide ?
|
|
0 : pTimeInquireInd->requesting_node_id;
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfTimeRemainingRequest(
|
|
pTimeInquireInd->conference_id,
|
|
60*60,
|
|
node_id);
|
|
TRACE_OUT(("HandleTimeInquireIndication: Failed to return Time Remaining, gcc_rc=%u", (UINT) GCCrc));
|
|
|
|
DebugExitVOID(HandleTimeInquireIndication);
|
|
}
|
|
|
|
|
|
BOOL DCRNCConferenceManager::
|
|
FindSocketNumber
|
|
(
|
|
GCCNodeID nid,
|
|
SOCKET *socket_number
|
|
)
|
|
{
|
|
// Currently we are relying on the fact there is only one conference at a time.
|
|
PCONFERENCE pConf = m_ConfList.PeekHead();
|
|
if (NULL != pConf)
|
|
{
|
|
return pConf->FindSocketNumber(nid, socket_number);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* H A N D L E A P P L I C A T I O N I N V O K E I N D I C A T I O N */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: HandleApplicationInvokeIndication
|
|
|
|
TODO: use GCC_OBJECT_KEY instead of GCC_H221_NONSTANDARD_KEY
|
|
----------------------------------------------------------------------------*/
|
|
#define NUMBER_OF_INTERNAL_STD_APPLETS 2
|
|
typedef struct
|
|
{
|
|
ULONG cNodes;
|
|
const ULONG *aNodes;
|
|
APPLET_ID eAppletId;
|
|
}
|
|
INTERNAL_STD_INVOKE_APPLET;
|
|
|
|
|
|
static const ULONG c_T126ObjectID[] = {0,0,20,126,0,1}; // Whiteboard
|
|
static const ULONG c_T127ObjectID[] = {0,0,20,127,0,1}; // File Transfer
|
|
|
|
static INTERNAL_STD_INVOKE_APPLET s_aStdAppletInvokeInfo[NUMBER_OF_INTERNAL_STD_APPLETS] =
|
|
{
|
|
{ // T.126 Whiteboard
|
|
sizeof(c_T126ObjectID) / sizeof(c_T126ObjectID[0]),
|
|
&c_T126ObjectID[0],
|
|
APPLET_ID_WB
|
|
},
|
|
{ // T.127 File Transfer
|
|
sizeof(c_T127ObjectID) / sizeof(c_T127ObjectID[0]),
|
|
&c_T127ObjectID[0],
|
|
APPLET_ID_FT
|
|
},
|
|
};
|
|
|
|
void InvokeAppletEntity(GCCConfID, GCCNodeID, GCCAppProtocolEntity*);
|
|
int GetInternalStandardAppletInvokeFunction(ULONG, ULONG*);
|
|
|
|
|
|
void HandleApplicationInvokeIndication ( ApplicationInvokeIndicationMessage * pInvokeMessage )
|
|
{
|
|
DebugEntry(HandleApplicationInvokeIndication);
|
|
|
|
for (ULONG i = 0; i < pInvokeMessage->number_of_app_protocol_entities; i++)
|
|
{
|
|
InvokeAppletEntity(pInvokeMessage->conference_id,
|
|
pInvokeMessage->invoking_node_id,
|
|
pInvokeMessage->app_protocol_entity_list[i]);
|
|
}
|
|
|
|
DebugExitVOID(HandleApplicationInvokeIndication);
|
|
}
|
|
|
|
|
|
int GetInternalStandardAppletInvokeFunction(ULONG cNodes, ULONG aNodes[])
|
|
{
|
|
for (ULONG i = 0; i < sizeof(s_aStdAppletInvokeInfo) / sizeof(s_aStdAppletInvokeInfo[0]); i++)
|
|
{
|
|
INTERNAL_STD_INVOKE_APPLET *p = &s_aStdAppletInvokeInfo[i];
|
|
if (cNodes == p->cNodes)
|
|
{
|
|
if (0 == memcmp(aNodes, p->aNodes, cNodes * sizeof(ULONG)))
|
|
{
|
|
return (int)p->eAppletId;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void InvokeAppletEntity
|
|
(
|
|
GCCConfID nConfID,
|
|
GCCNodeID nidInitiator,
|
|
GCCAppProtocolEntity *pAppEntity
|
|
)
|
|
{
|
|
DebugEntry(InvokeAppletEntity);
|
|
|
|
int iAppletId;
|
|
HKEY hkey;
|
|
ULONG cNodes, cbDataSize, i;
|
|
ULONG *pNodeID;
|
|
LPOSTR postrNonStdKey;
|
|
LPBYTE pbData;
|
|
GCCSessionID sidApplet = pAppEntity->session_key.session_id;
|
|
CApplet *pApplet;
|
|
char szGuid[LENGTH_SZGUID_FORMATTED];
|
|
char szKey[MAX_PATH];
|
|
szKey[0] = '\0'; // safety net
|
|
|
|
// if (!pAppEntity->must_be_invoked)
|
|
// return; // this is optional and can fail
|
|
|
|
switch (pAppEntity->session_key.application_protocol_key.key_type)
|
|
{
|
|
case GCC_OBJECT_KEY:
|
|
//
|
|
// Standard object key
|
|
//
|
|
cNodes = pAppEntity->session_key.application_protocol_key.object_id.long_string_length;
|
|
pNodeID = pAppEntity->session_key.application_protocol_key.object_id.long_string;
|
|
|
|
// check if it is an internal standard applet
|
|
iAppletId = GetInternalStandardAppletInvokeFunction(cNodes, pNodeID);
|
|
if (iAppletId >= 0)
|
|
{
|
|
// Invoke the internal applet
|
|
WARNING_OUT(("Find internal standard applet %s.\n",
|
|
iAppletId?"File Transfer":"White Board"));
|
|
T120_LoadApplet((APPLET_ID)iAppletId, FALSE, 0, FALSE, NULL);
|
|
return;
|
|
}
|
|
|
|
// ok, it is not an internal applet, convert it to hexa-dot string to look for
|
|
// a registered third-party applet
|
|
// Format: T120_APPLET_KEY\T120_STD_KEY\{hex-dot string} '\0'
|
|
if (0 < cNodes && NULL != pNodeID &&
|
|
(cNodes << 2) + sizeof(T120_APPLET_KEY) + sizeof(T120_STD_KEY) < MAX_PATH - 2)
|
|
{
|
|
::wsprintfA(szKey, "%s\\%s\\%s", T120_APPLET_KEY, T120_STD_KEY, "{");
|
|
LPSTR pszKey = szKey + ::lstrlenA(szKey);
|
|
for (i = 0; i < cNodes; i++, pNodeID++)
|
|
{
|
|
::wsprintf(pszKey, "%08X.", (UINT) *pNodeID);
|
|
pszKey += ::lstrlenA(pszKey);
|
|
}
|
|
::wsprintfA(pszKey-1, "%s", "}"); // remove the last dot character
|
|
WARNING_OUT(("Find standard applet: %s\n", szKey));
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("InvokeAppletEntity: cannot handle standard key size=%u", cNodes));
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case GCC_H221_NONSTANDARD_KEY:
|
|
//
|
|
// Non-standard object key
|
|
//
|
|
postrNonStdKey = &pAppEntity->session_key.application_protocol_key.h221_non_standard_id;
|
|
if (GetGuidFromH221AppKey(szGuid, postrNonStdKey))
|
|
{
|
|
//
|
|
// Microsoft non-standard object key
|
|
// NetMeeting's DataChannel
|
|
//
|
|
::wsprintfA(szKey, "%s\\%s", GUID_KEY, szGuid);
|
|
WARNING_OUT(("Find Microsoft non-standard applet: %s\n", szKey));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Non-Microsoft non-standard object key
|
|
//
|
|
|
|
// Third-party's non-standard object key.
|
|
// In this case, we convert the octet string into dotted decimal string,
|
|
// like an IP address.
|
|
// Each byte can take four characters in the dotted decimal string.
|
|
// Format: T120_APPLET_KEY\T120_NONSTD_KEY\{hex-dot string}'\0'
|
|
cbDataSize = postrNonStdKey->length;
|
|
pbData = postrNonStdKey->value;
|
|
if (0 < cbDataSize && NULL != pbData &&
|
|
(cbDataSize << 2) + sizeof(T120_APPLET_KEY) + sizeof(T120_NONSTD_KEY) < MAX_PATH - 2)
|
|
{
|
|
::wsprintfA(szKey, "%s\\%s\\%s", T120_APPLET_KEY, T120_NONSTD_KEY, "{");
|
|
LPSTR pszKey = szKey + ::lstrlenA(szKey);
|
|
for (i = 0; i < cbDataSize; i++, pbData++)
|
|
{
|
|
::wsprintfA(pszKey, "%02X.", (UINT) *pbData);
|
|
pszKey += ::lstrlenA(pszKey);
|
|
}
|
|
::wsprintfA(pszKey-1, "%s", "}"); // remove the last dot character
|
|
WARNING_OUT(("Find third party non-standard applet: %s\n", szKey));
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("InvokeAppletEntity: cannot handle non-std key size=%u", cbDataSize));
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("InvokeAppletEntity: invalid object key type=%u", pAppEntity->session_key.application_protocol_key.key_type));
|
|
return;
|
|
}
|
|
|
|
// Look for the registry key. open the registry now
|
|
RegEntry GuidKey(szKey, HKEY_LOCAL_MACHINE, FALSE, KEY_READ);
|
|
if (NO_ERROR == GuidKey.GetError())
|
|
{
|
|
LPSTR szAppName = ::My_strdupA(GuidKey.GetString(REGVAL_GUID_APPNAME));
|
|
LPSTR szCmdLine = ::My_strdupA(GuidKey.GetString(REGVAL_GUID_CMDLINE));
|
|
LPSTR szCurrDir = ::My_strdupA(GuidKey.GetString(REGVAL_GUID_CURRDIR));
|
|
|
|
if ((NULL != szAppName) || (NULL != szCmdLine))
|
|
{
|
|
LPSTR lpEnv;
|
|
STARTUPINFO startupInfo;
|
|
PROCESS_INFORMATION processInfo;
|
|
char szEnv[32];
|
|
|
|
::ZeroMemory(&processInfo, sizeof(processInfo));
|
|
::ZeroMemory(&startupInfo, sizeof(startupInfo));
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
|
|
// set the special environment variables
|
|
::wsprintfA(szEnv, "%u", nConfID);
|
|
SetEnvironmentVariable(ENV_CONFID, szEnv);
|
|
::wsprintfA(szEnv, "%u", nidInitiator);
|
|
SetEnvironmentVariable(ENV_NODEID, szEnv);
|
|
|
|
lpEnv = ::GetEnvironmentStrings();
|
|
|
|
::CreateProcess(
|
|
szAppName, // pointer to name of executable module
|
|
szCmdLine, // pointer to command line string
|
|
NULL, // pointer to process security attributes
|
|
NULL, // pointer to thread security attributes
|
|
FALSE, // handle inheritance flag
|
|
0, // creation flags
|
|
lpEnv, // pointer to new environment block
|
|
szCurrDir, // pointer to current directory name
|
|
&startupInfo, // pointer to STARTUPINFO
|
|
&processInfo); // pointer to PROCESS_INFORMATION
|
|
|
|
if (NULL != lpEnv)
|
|
{
|
|
::FreeEnvironmentStrings(lpEnv);
|
|
}
|
|
}
|
|
|
|
delete szAppName;
|
|
delete szCmdLine;
|
|
delete szCurrDir;
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("InvokeAppletEntity: no such registry=[%s]", szKey));
|
|
}
|
|
|
|
DebugExitVOID(InvokeAppletEntity);
|
|
}
|
|
|
|
|
|
LPWSTR GetNodeName(void)
|
|
{
|
|
LPWSTR pwszName;
|
|
LPSTR pszName;
|
|
RegEntry NameKey(ISAPI_KEY "\\" REGKEY_USERDETAILS);
|
|
|
|
if (g_bRDS) // Running as service?
|
|
{
|
|
char szName[MAX_COMPUTERNAME_LENGTH+2] = "";
|
|
DWORD dwBuf = sizeof(szName);
|
|
if ( !GetComputerName((LPSTR)&szName,&dwBuf) )
|
|
{
|
|
ERROR_OUT(("GetNameName: GetComputerName failed"));
|
|
}
|
|
pwszName = ::AnsiToUnicode(szName);
|
|
}
|
|
else
|
|
{
|
|
pszName = NameKey.GetString(REGVAL_ULS_NAME);
|
|
pwszName = ::AnsiToUnicode(pszName);
|
|
}
|
|
|
|
if (::IsEmptyStringW(pwszName))
|
|
{
|
|
WARNING_OUT(("GetNodeName: No node name"));
|
|
delete pwszName;
|
|
pwszName = NULL;
|
|
}
|
|
|
|
TRACE_OUT(("GetNodeName: pszName=%s", pszName));
|
|
return pwszName;
|
|
}
|
|
|
|
|
|
// Update <NodeId,Name> pair
|
|
void DCRNCConferenceManager::
|
|
UpdateNodeIdNameListAndUserData(GCCMessage * pGCCMessage)
|
|
{
|
|
GCCConfID ConfId = pGCCMessage->nConfID;
|
|
PCONFERENCE pConf = GetConferenceFromID(ConfId);
|
|
if (pConf)
|
|
pConf->UpdateNodeIdNameListAndUserData(pGCCMessage);
|
|
}
|
|
|
|
|
|
// Query node name
|
|
ULONG DCRNCConferenceManager::
|
|
GetNodeName(GCCConfID ConfId, GCCNodeID NodeId,
|
|
LPSTR pszBuffer, ULONG cbBufSize)
|
|
{
|
|
PCONFERENCE pConf = GetConferenceFromID(ConfId);
|
|
if (pConf)
|
|
return pConf->GetNodeName(NodeId, pszBuffer, cbBufSize);
|
|
return 0;
|
|
}
|
|
|
|
// Query user data
|
|
ULONG DCRNCConferenceManager::
|
|
GetUserGUIDData(GCCConfID ConfId, GCCNodeID NodeId,
|
|
GUID *pGuid, LPBYTE pbBuffer, ULONG cbBufSize)
|
|
{
|
|
PCONFERENCE pConf = GetConferenceFromID(ConfId);
|
|
if (pConf)
|
|
return pConf->GetUserGUIDData(NodeId, pGuid, pbBuffer, cbBufSize);
|
|
return 0;
|
|
}
|
|
|
|
|
|
ULONG WINAPI T120_GetNodeName(GCCConfID ConfId, GCCNodeID NodeId,
|
|
LPSTR pszBuffer, ULONG cbBufSize)
|
|
{
|
|
return g_pNCConfMgr->GetNodeName(ConfId, NodeId, pszBuffer, cbBufSize);
|
|
}
|
|
|
|
ULONG WINAPI T120_GetUserData(GCCConfID ConfId, GCCNodeID NodeId,
|
|
GUID *pGuid, LPBYTE pbBuffer,
|
|
ULONG cbBufSize)
|
|
{
|
|
return g_pNCConfMgr->GetUserGUIDData(ConfId, NodeId, pGuid, pbBuffer, cbBufSize);
|
|
}
|
|
|