2189 lines
64 KiB
C++
2189 lines
64 KiB
C++
/****************************************************************************/
|
|
/* */
|
|
/* ERNCCONF.CPP */
|
|
/* */
|
|
/* Base Conference class for the Reference System Node Controller. */
|
|
/* */
|
|
/* Copyright Data Connection Ltd. 1995 */
|
|
/* */
|
|
/****************************************************************************/
|
|
/* Changes: */
|
|
/* */
|
|
/* 12Jul95 NFC Created. */
|
|
/* 05Oct95 NFC SFR 6206 Treat a "Join" as an incoming call. */
|
|
/* 11Oct95 PM Relax checks on conference termination to */
|
|
/* prevent "no win" situations */
|
|
/* Support START_ALTERNATE from TPhys API */
|
|
/* */
|
|
/****************************************************************************/
|
|
#include "precomp.h"
|
|
DEBUG_FILEZONE(ZONE_GCC_NC);
|
|
#include "ernccons.h"
|
|
#include "nccglbl.hpp"
|
|
#include "erncvrsn.hpp"
|
|
#include <cuserdta.hpp>
|
|
|
|
#include "connect.h"
|
|
#include "erncconf.hpp"
|
|
#include "ernctrc.h"
|
|
#include "ernccm.hpp"
|
|
#include <iappldr.h>
|
|
#include "plgxprt.h"
|
|
#include "nmremote.h"
|
|
|
|
|
|
extern PController g_pMCSController;
|
|
|
|
DCRNCConference::
|
|
DCRNCConference
|
|
(
|
|
LPCWSTR pwcszConfName,
|
|
GCCConfID nConfID,
|
|
BOOL fSecure,
|
|
HRESULT *pRetCode
|
|
)
|
|
:
|
|
CRefCount(MAKE_STAMP_ID('N','C','C','F')),
|
|
m_fNotifyToDo(FALSE),
|
|
m_fActive(TRUE),
|
|
#ifdef _DEBUG
|
|
m_fAppendedToConfList(FALSE),
|
|
#endif
|
|
m_pInviteUI(NULL),
|
|
m_pszFirstRemoteNodeAddress(NULL),
|
|
m_nConfID(nConfID),
|
|
m_eState(CONF_ST_UNINITIALIZED),
|
|
m_fIncoming(FALSE),
|
|
m_pbHashedPassword(NULL),
|
|
m_cbHashedPassword(0),
|
|
m_pwszPassword(NULL),
|
|
m_pszNumericPassword(NULL),
|
|
// T120 conference
|
|
m_eT120State(T120C_ST_IDLE),
|
|
m_nidMyself(0),
|
|
m_fSecure(fSecure),
|
|
m_nInvalidPasswords(0)
|
|
{
|
|
DebugEntry(DCRNCConference::DCRNCConference);
|
|
|
|
// Save the conference name.
|
|
DBG_SAVE_FILE_LINE
|
|
m_pwszConfName = ::My_strdupW(pwcszConfName);
|
|
if (! ::IsEmptyStringW(m_pwszConfName))
|
|
{
|
|
*pRetCode = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
*pRetCode = (NULL == m_pwszConfName) ? UI_RC_OUT_OF_MEMORY :
|
|
UI_RC_NO_CONFERENCE_NAME;
|
|
}
|
|
|
|
// T120 conference
|
|
m_ConfName.numeric_string = NULL;
|
|
m_ConfName.text_string = NULL;
|
|
|
|
DebugExitVOID(DCRNCConference::DCRNCConference);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Destructor - see erncconf.h */
|
|
/****************************************************************************/
|
|
DCRNCConference::
|
|
~DCRNCConference(void)
|
|
{
|
|
DebugEntry(DCRNCConference::~DCRNCConference);
|
|
|
|
ASSERT(! m_fAppendedToConfList);
|
|
|
|
// delete all the name strings
|
|
LPSTR pszStr;
|
|
while (NULL != (pszStr = m_NodeIdNameList.Get()))
|
|
{
|
|
delete [] pszStr;
|
|
}
|
|
|
|
// Delete all the usr data
|
|
CNCUserDataList *pUserDataList;
|
|
while (NULL != (pUserDataList = m_UserDataList.Get()))
|
|
{
|
|
delete pUserDataList;
|
|
}
|
|
|
|
delete m_pwszConfName;
|
|
|
|
// If there is a password, delete it.
|
|
delete []m_pbHashedPassword;
|
|
delete m_pwszPassword;
|
|
delete m_pszNumericPassword;
|
|
|
|
delete m_pszFirstRemoteNodeAddress;
|
|
|
|
// T120 conference
|
|
delete m_ConfName.numeric_string;
|
|
|
|
DebugExitVOID(DCRNCConference::~DCRNCConference);
|
|
}
|
|
|
|
|
|
void DCRNCConference::
|
|
OnRemoved(BOOL fReleaseNow)
|
|
{
|
|
DebugEntry(DCRNCConference::OnRemoved);
|
|
|
|
CLogicalConnection *pConEntry;
|
|
|
|
#ifdef _DEBUG
|
|
m_fAppendedToConfList = FALSE;
|
|
#endif
|
|
|
|
// Issue a request to leave the conference.
|
|
// This request may fail, but may as well let leave validate
|
|
// itself, rather than put an extra check in here.
|
|
// See comments in RemoveConference() and Leave() for more details
|
|
// if interested.
|
|
if (T120C_ST_PENDING_DISCONNECT != m_eT120State &&
|
|
T120C_ST_PENDING_TERMINATE != m_eT120State)
|
|
{
|
|
Leave();
|
|
}
|
|
|
|
// Take the conference out of the list of pending invites.
|
|
g_pNCConfMgr->RemoveInviteIndWorkItem(m_pInviteUI);
|
|
|
|
// End all physical connections in use by this conference,
|
|
// and inform the user of the results of pending events.
|
|
while (NULL != (pConEntry = m_ConnList.Get()))
|
|
{
|
|
pConEntry->Delete(UI_RC_CONFERENCE_GOING_DOWN);
|
|
}
|
|
|
|
//
|
|
// LONCHANC: This destructor 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.
|
|
//
|
|
|
|
// ASSERT(2 == GetRefCount());
|
|
|
|
// Tell UI its handle to conference is no longer valid.
|
|
if (NULL != g_pCallbackInterface)
|
|
{
|
|
g_pCallbackInterface->OnConferenceEnded((CONF_HANDLE) this);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConference::OnRemoved: g_pCallbackInterface is null"));
|
|
}
|
|
|
|
// ASSERT(1 == GetRefCount());
|
|
|
|
if (fReleaseNow)
|
|
{
|
|
ReleaseNow();
|
|
}
|
|
else
|
|
{
|
|
Release();
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConference::OnRemoved);
|
|
}
|
|
|
|
|
|
//
|
|
// IDataConference Interface
|
|
//
|
|
|
|
|
|
STDMETHODIMP_(void) DCRNCConference::
|
|
ReleaseInterface(void)
|
|
{
|
|
DebugEntry(DCRNCConference::ReleaseInterface);
|
|
InterfaceEntry();
|
|
|
|
Release();
|
|
|
|
DebugExitVOID(DCRNCConference::ReleaseInterface);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(UINT_PTR) DCRNCConference::
|
|
GetConferenceID(void)
|
|
{
|
|
DebugEntry(DCRNCConference::GetConferenceID);
|
|
InterfaceEntry();
|
|
|
|
DebugExitINT(DCRNCConference::GetConferenceID, (UINT) m_nConfID);
|
|
return m_nConfID;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
Leave(void)
|
|
{
|
|
DebugEntry(DCRNCConference::Leave);
|
|
InterfaceEntry();
|
|
|
|
GCCError GCCrc;
|
|
HRESULT hr;
|
|
|
|
switch (m_eT120State)
|
|
{
|
|
// LONCHANC: Added the following two cases for cancellation.
|
|
case T120C_ST_PENDING_START_CONFIRM:
|
|
case T120C_ST_PENDING_JOIN_CONFIRM:
|
|
|
|
case T120C_ST_PENDING_ROSTER_ENTRY:
|
|
case T120C_ST_PENDING_ROSTER_MESSAGE:
|
|
case T120C_ST_PENDING_ANNOUNCE_PERMISSION:
|
|
|
|
// User has called leave on a conference when it is being brought up.
|
|
// Drop through to issue a disconnect request to T120.
|
|
|
|
case T120C_ST_CONF_STARTED:
|
|
|
|
// Set the state of the conference to note that we are
|
|
// disconnecting from T120.
|
|
// LONCHANC: this is a must to avoid reentrance of this Leave()
|
|
// when direct InviteConfirm hits Node Controller later.
|
|
m_eT120State = T120C_ST_PENDING_DISCONNECT;
|
|
|
|
// User has requested to leave the conference after it has been
|
|
// started as a T120 conference, so ask T120 to end the conference
|
|
// before removing internal data structures.
|
|
GCCrc = g_pIT120ControlSap->ConfDisconnectRequest(m_nConfID);
|
|
hr = ::GetGCCRCDetails(GCCrc);
|
|
TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfDisconnectRequest, rc=%d", GCCrc));
|
|
if (NO_ERROR == hr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// T120 won't let us leave a conference that we think we are in.
|
|
// Take this to mean that T120 doesn't know about the conference
|
|
// anymore and just destroy our own knowledge of the conference.
|
|
WARNING_OUT(("DCRNCConference::Leave: Failed to leave conference, GCC error %d", GCCrc));
|
|
|
|
// Drop through to destroy our references.
|
|
|
|
case T120C_ST_IDLE:
|
|
|
|
// User has requested to leave a conference that has not been
|
|
// started.
|
|
// This should only happen when told that a conference join
|
|
// request supplied an invalid password and the user gives up
|
|
// on attempting to join the conference (or shuts down conferencing).
|
|
// Just do the same processing as would be done when a T120
|
|
// disconnect confirmation fires.
|
|
g_pNCConfMgr->RemoveConference(this);
|
|
hr = NO_ERROR;
|
|
break;
|
|
|
|
case T120C_ST_PENDING_DISCONNECT:
|
|
case T120C_ST_PENDING_TERMINATE:
|
|
|
|
// User has requested to leave a conference that is already
|
|
// going down (most likely because of a prior request to leave).
|
|
hr = UI_RC_CONFERENCE_GOING_DOWN;
|
|
WARNING_OUT(("DCRNCConference::Leave: conference already going down, state=%d", m_eT120State));
|
|
break;
|
|
|
|
default:
|
|
|
|
// User has called leave on a conference when he shouldn't
|
|
// (e.g. when it is being brought up).
|
|
// This is very unlikely to happen as the user doesn't know
|
|
// the conference handle at this point.
|
|
hr = UI_RC_INVALID_REQUEST;
|
|
ERROR_OUT(("DCRNCConference::Leave: invalid state=%d", m_eT120State));
|
|
break;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::Leave, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
EjectUser ( UINT nidEjected )
|
|
{
|
|
DebugEntry(DCRNCConference::EjectUser);
|
|
InterfaceEntry();
|
|
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfEjectUserRequest(m_nConfID, (UserID) nidEjected, GCC_REASON_USER_INITIATED);
|
|
HRESULT hr = ::GetGCCRCDetails(GCCrc);
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("DCRNCConference::EjectUser: Failed to eject user conference, GCC error %d", GCCrc));
|
|
}
|
|
|
|
CLogicalConnection *pConEntry = GetConEntryByNodeID((GCCNodeID) nidEjected);
|
|
if (NULL != pConEntry)
|
|
{
|
|
pConEntry->Delete(UI_RC_USER_DISCONNECTED);
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::EjectUser, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
Invite
|
|
(
|
|
LPCSTR pcszNodeAddress,
|
|
USERDATAINFO aInfo[],
|
|
UINT cInfo,
|
|
REQUEST_HANDLE * phRequest
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConference::Invite);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
#if defined(TEST_PLUGGABLE) && defined(_DEBUG)
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
pcszNodeAddress = ::FakeNodeAddress(pcszNodeAddress);
|
|
}
|
|
#endif
|
|
|
|
if (NULL != pcszNodeAddress && NULL != phRequest)
|
|
{
|
|
// if winsock is disabled, block any IP address or machine name
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
if (! IsValidPluggableTransportName(pcszNodeAddress))
|
|
{
|
|
return UI_RC_NO_WINSOCK;
|
|
}
|
|
}
|
|
|
|
// Check that person is not already in the conference.
|
|
if (GetConEntry((LPSTR) pcszNodeAddress))
|
|
{
|
|
hr = UI_RC_ALREADY_IN_CONFERENCE;
|
|
}
|
|
else
|
|
{
|
|
hr = StartConnection((LPSTR) pcszNodeAddress,
|
|
CONF_CON_PENDING_INVITE,
|
|
aInfo,
|
|
cInfo,
|
|
m_fSecure,
|
|
phRequest);
|
|
}
|
|
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("Error adding connection"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = (pcszNodeAddress == NULL) ? UI_RC_NO_ADDRESS : UI_RC_BAD_PARAMETER;
|
|
ERROR_OUT(("DCRNCConference::Invite: invalid parameters, hr=0x%x", (UINT) hr));
|
|
}
|
|
|
|
// Sit and wait for the connection to complete before continuing.
|
|
DebugExitHRESULT(DCRNCConference::Invite, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
CancelInvite ( REQUEST_HANDLE hRequest )
|
|
{
|
|
DebugEntry(DCRNCConference::CancelInvite);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
CLogicalConnection *pConEntry = (CLogicalConnection *) hRequest;
|
|
|
|
if (NULL != pConEntry)
|
|
{
|
|
ConnectionHandle hConn = pConEntry->GetInviteReqConnHandle();
|
|
ASSERT(NULL != hConn);
|
|
g_pIT120ControlSap->CancelInviteRequest(m_nConfID, hConn);
|
|
hr = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::CancelInvite, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
GetCred ( PBYTE *ppbCred, DWORD *pcbCred )
|
|
{
|
|
DebugEntry(DCRNCConference::GetCred);
|
|
HRESULT hr = UI_RC_INTERNAL_ERROR;
|
|
if (m_pbCred)
|
|
{
|
|
*ppbCred = m_pbCred;
|
|
*pcbCred = m_cbCred;
|
|
hr = NO_ERROR;
|
|
}
|
|
DebugExitHRESULT(DCRNCConference::GetCred, hr);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
InviteResponse ( BOOL fResponse )
|
|
{
|
|
DebugEntry(DCRNCConference::InviteResponse);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hrResponse = fResponse ? NO_ERROR : UI_RC_USER_REJECTED;
|
|
|
|
HRESULT hr = InviteResponse(hrResponse);
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::InviteResponse, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT DCRNCConference::
|
|
InviteResponse ( HRESULT hrResponse )
|
|
{
|
|
DebugEntry(DCRNCConference::InviteResponse);
|
|
InterfaceEntry();
|
|
|
|
GCCResult Result = ::MapRCToGCCResult(hrResponse);
|
|
GCCError GCCrc = g_pIT120ControlSap->ConfInviteResponse(
|
|
m_nConfID,
|
|
NULL,
|
|
m_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
|
|
Result);
|
|
if ((GCCrc == GCC_RESULT_SUCCESSFUL) && (Result == GCC_RESULT_SUCCESSFUL))
|
|
{
|
|
// Have successfully posted an invite response acceptance.
|
|
// Note that the conference is expecting permission to
|
|
// announce its presence.
|
|
m_eT120State = T120C_ST_PENDING_ANNOUNCE_PERMISSION;
|
|
}
|
|
else
|
|
{
|
|
// Have rejected/failed a request to be invited into a conference.
|
|
// Remove the references that were created to track the potential
|
|
// new conference.
|
|
g_pNCConfMgr->RemoveConference(this);
|
|
}
|
|
|
|
HRESULT hr = ::GetGCCRCDetails(GCCrc);
|
|
|
|
DebugExitHRESULT(DCRNCConferenceManager::InviteResponse, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
JoinResponse ( BOOL fResponse )
|
|
{
|
|
DebugEntry(DCRNCConference::JoinResponse);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
CJoinIndWork *pJoinUI = g_pNCConfMgr->PeekFirstJoinIndWorkItem();
|
|
if (NULL != pJoinUI)
|
|
{
|
|
if (pJoinUI->GetConference() == this)
|
|
{
|
|
if (fResponse && pJoinUI->GetConEntry()->NewLocalAddress())
|
|
{
|
|
AnnouncePresence();
|
|
}
|
|
hr = pJoinUI->Respond(fResponse ? GCC_RESULT_SUCCESSFUL : GCC_RESULT_USER_REJECTED);
|
|
// Done responding to event, so can now remove from list and process
|
|
// another pending event.
|
|
// Note: since the handling of the previous event is still
|
|
// potentially on the stack, this can cause the stack to grow,
|
|
// but this should not be a problem for Win32.
|
|
g_pNCConfMgr->RemoveJoinIndWorkItem(pJoinUI);
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConference::JoinResponse: Empty m_JoinIndWorkList, fResponse=%u", fResponse));
|
|
hr = UI_RC_INTERNAL_ERROR;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::JoinResponse, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
LaunchGuid
|
|
(
|
|
const GUID *pcGUID,
|
|
UINT auNodeIDs[],
|
|
UINT cNodes
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConference::LaunchGuid);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
if (NULL != pcGUID)
|
|
{
|
|
//
|
|
// We probably should support conference-wide app invoke by
|
|
// cNodes==0 and auNodeIDs==NULL.
|
|
// Implement it later...
|
|
//
|
|
if ((0 != cNodes) || (NULL != auNodeIDs))
|
|
{
|
|
// UserID is a short. We have to translate these UserID to a new array.
|
|
// Try not to allocate memory for small array.
|
|
UserID *pNodeIDs;
|
|
const UINT c_cRemote = 16;
|
|
UserID auidRemote[c_cRemote];
|
|
if (cNodes <= c_cRemote)
|
|
{
|
|
pNodeIDs = auidRemote;
|
|
}
|
|
else
|
|
{
|
|
pNodeIDs = new UserID[cNodes];
|
|
if (NULL == pNodeIDs)
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
goto MyExit;
|
|
}
|
|
}
|
|
|
|
// Copy all the node IDs.
|
|
for (UINT i = 0; i < cNodes; i++)
|
|
{
|
|
pNodeIDs[i] = (UserID)auNodeIDs[i];
|
|
}
|
|
|
|
// Construct the key
|
|
GCCError GCCrc;
|
|
GCCObjectKey * pAppKey;
|
|
GCCAppProtocolEntity AppEntity;
|
|
GCCAppProtocolEntity * pAppEntity;
|
|
|
|
BYTE h221Key[CB_H221_GUIDKEY];
|
|
::CreateH221AppKeyFromGuid(h221Key, (GUID *) pcGUID);
|
|
|
|
::ZeroMemory(&AppEntity, sizeof(AppEntity));
|
|
pAppKey = &AppEntity.session_key.application_protocol_key;
|
|
pAppKey->key_type = GCC_H221_NONSTANDARD_KEY;
|
|
pAppKey->h221_non_standard_id.length = sizeof(h221Key);
|
|
pAppKey->h221_non_standard_id.value = h221Key;
|
|
|
|
// AppEntity.session_key.session_id = 0; // default session
|
|
// AppEntity.number_of_expected_capabilities = 0; // no capabilities
|
|
// AppEntity.expected_capabilities_list = NULL;
|
|
AppEntity.startup_channel_type = MCS_NO_CHANNEL_TYPE_SPECIFIED;
|
|
AppEntity.must_be_invoked = TRUE;
|
|
|
|
pAppEntity = &AppEntity;
|
|
|
|
GCCrc = g_pIT120ControlSap->AppletInvokeRequest(m_nConfID, 1, &pAppEntity, cNodes, pNodeIDs);
|
|
|
|
hr = ::GetGCCRCDetails(GCCrc);
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("DCRNCConference::LaunchGuid: AppletInvokeRequest failed, GCCrc=%u", GCCrc));
|
|
}
|
|
|
|
if (pNodeIDs != auidRemote)
|
|
{
|
|
delete [] pNodeIDs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
ERROR_OUT(("DCRNCConference::LaunchGuid: invalid combination, cNodes=%u. auNodeIDs=0x%p", cNodes, auNodeIDs));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
ERROR_OUT(("DCRNCConference::LaunchGuid: null pcGUID"));
|
|
}
|
|
|
|
MyExit:
|
|
|
|
DebugExitHRESULT(DCRNCConference::LaunchGuid, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
SetUserData
|
|
(
|
|
const GUID *pcGUID,
|
|
UINT cbData,
|
|
LPVOID pData
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConference::SetUserData);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
|
|
if (0 != cbData || NULL != pData)
|
|
{
|
|
hr = m_LocalUserData.AddUserData((GUID *) pcGUID, cbData, pData);
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_BAD_PARAMETER;
|
|
ERROR_OUT(("DCRNCConference::SetUserData: invalid combination, cbData=%u. pData=0x%p", cbData, pData));
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::SetUserData, hr);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP_(BOOL) DCRNCConference::
|
|
IsSecure ()
|
|
{
|
|
return m_fSecure;
|
|
}
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
SetSecurity ( BOOL fSecure )
|
|
{
|
|
m_fSecure = fSecure;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
UpdateUserData(void)
|
|
{
|
|
DebugEntry(DCRNCConference::UpdateUserData);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr = AnnouncePresence();
|
|
|
|
DebugExitHRESULT(DCRNCConference::UpdateUserData, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DCRNCConference::
|
|
GetLocalAddressList
|
|
(
|
|
LPWSTR pwszBuffer,
|
|
UINT cchBuffer
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConference::GetLocalAddressList);
|
|
InterfaceEntry();
|
|
|
|
HRESULT hr;
|
|
UINT cAddrs;
|
|
LPCSTR *pAddresses = NULL;
|
|
|
|
ASSERT(cchBuffer > 1); // buffer should have enough room for a double NULL terminator
|
|
|
|
hr = m_LocalAddressList.GetLocalAddressList(&cAddrs, &pAddresses);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
LPWSTR pwszPos = pwszBuffer;
|
|
for (UINT i = 0; i < cAddrs; i++)
|
|
{
|
|
ASSERT(pAddresses[i]);
|
|
LPWSTR pwszAddress = ::AnsiToUnicode(pAddresses[i]);
|
|
UINT cchAddress = ::My_strlenW(pwszAddress);
|
|
if ((cchBuffer - (pwszPos - pwszBuffer)) <
|
|
(RNC_GCC_TRANSPORT_AND_SEPARATOR_LENGTH + cchAddress + 2))
|
|
{
|
|
// NOTE: +2 insures room for the two '\0' chars
|
|
// If there isn't room, break out here:
|
|
break;
|
|
}
|
|
LStrCpyW(pwszPos, RNC_GCC_TRANSPORT_AND_SEPARATOR_UNICODE);
|
|
pwszPos += RNC_GCC_TRANSPORT_AND_SEPARATOR_LENGTH;
|
|
LStrCpyW(pwszPos, pwszAddress);
|
|
pwszPos += cchAddress;
|
|
*pwszPos = L'\0';
|
|
pwszPos++;
|
|
delete pwszAddress;
|
|
}
|
|
if ((UINT)(pwszPos - pwszBuffer) < cchBuffer)
|
|
{
|
|
*pwszPos = L'\0';
|
|
}
|
|
if (0 == cAddrs)
|
|
{
|
|
// No addresses in the string, so insure that the string returned is L"\0\0"
|
|
pwszPos[1] = L'\0';
|
|
}
|
|
delete [] pAddresses;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConference::GetLocalAddressList: GetLocalAddressList failed, hr=0x%x", (UINT) hr));
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::GetLocalAddressList, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(UINT) DCRNCConference::
|
|
GetParentNodeID(void)
|
|
{
|
|
DebugEntry(DCRNCConference::GetConferenceID);
|
|
InterfaceEntry();
|
|
|
|
GCCNodeID nidParent = 0;
|
|
g_pIT120ControlSap->GetParentNodeID(m_nConfID, &nidParent);
|
|
|
|
DebugExitINT(DCRNCConference::GetConferenceID, (UINT) nidParent);
|
|
return (UINT) nidParent;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLogicalConnection * DCRNCConference::
|
|
GetConEntry ( ConnectionHandle hInviteIndConn )
|
|
{
|
|
CLogicalConnection *pConEntry = NULL;
|
|
m_ConnList.Reset();
|
|
while (NULL != (pConEntry = m_ConnList.Iterate()))
|
|
{
|
|
if (pConEntry->GetInviteReqConnHandle() == hInviteIndConn)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return pConEntry;
|
|
}
|
|
|
|
|
|
CLogicalConnection * DCRNCConference::
|
|
GetConEntry ( LPSTR pszNodeAddress )
|
|
{
|
|
CLogicalConnection *pConEntry = NULL;
|
|
m_ConnList.Reset();
|
|
while (NULL != (pConEntry = m_ConnList.Iterate()))
|
|
{
|
|
if (0 == ::lstrcmpA(pConEntry->GetNodeAddress(), pszNodeAddress))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return pConEntry;
|
|
}
|
|
|
|
|
|
CLogicalConnection * DCRNCConference::
|
|
GetConEntryByNodeID ( GCCNodeID nid )
|
|
{
|
|
CLogicalConnection *pConEntry = NULL;
|
|
m_ConnList.Reset();
|
|
while (NULL != (pConEntry = m_ConnList.Iterate()))
|
|
{
|
|
if (nid == pConEntry->GetConnectionNodeID())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return pConEntry;
|
|
}
|
|
|
|
|
|
|
|
void DCRNCConference::
|
|
FirstRoster(void)
|
|
{
|
|
DebugEntry(DCRNCConference::FirstRoster);
|
|
|
|
// Great! We are now in a conference and outside of any
|
|
// T120 callback, so that calling back into T120 will not
|
|
// deadlock applications.
|
|
// Let the applications know about the conference,
|
|
// and then ask for a roster update.
|
|
if (m_eT120State == T120C_ST_PENDING_ROSTER_MESSAGE)
|
|
{
|
|
m_eT120State = T120C_ST_CONF_STARTED;
|
|
NotifyConferenceComplete(NO_ERROR);
|
|
RefreshRoster();
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConference::FirstRoster);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* HandleGCCCallback() - see erncconf.h */
|
|
/****************************************************************************/
|
|
// LONCHANC: Merged to T120 Conference.
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ValidatePassword() - Validates a join request by checking the supplied */
|
|
/* password with the one set when the conference was setup. */
|
|
/****************************************************************************/
|
|
BOOL DCRNCConference::
|
|
ValidatePassword ( GCCChallengeRequestResponse *pPasswordChallenge )
|
|
{
|
|
PBYTE pbPasswordChallenge = NULL;
|
|
DWORD cbPasswordChallenge = 0;
|
|
CHash hashObj;
|
|
OSVERSIONINFO osvi;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
if (FALSE == ::GetVersionEx (&osvi))
|
|
{
|
|
ERROR_OUT(("GetVersionEx() failed!"));
|
|
}
|
|
|
|
if (!(VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && g_bRDS) &&
|
|
(NULL == m_pbHashedPassword) && (NULL == m_pszNumericPassword) && (NULL == m_pwszPassword))
|
|
{
|
|
return TRUE;
|
|
}
|
|
if ((pPasswordChallenge == NULL) ||
|
|
(pPasswordChallenge->password_challenge_type != GCC_PASSWORD_IN_THE_CLEAR))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We are going to verify the password as a logon
|
|
//
|
|
|
|
if ( VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && g_bRDS)
|
|
{
|
|
BYTE InfoBuffer[1024];
|
|
PTOKEN_GROUPS ptgGroups = (PTOKEN_GROUPS)InfoBuffer;
|
|
HANDLE hToken;
|
|
BOOL bSuccess = FALSE;
|
|
DWORD dwInfoBufferSize;
|
|
PSID psidAdministrators;
|
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
CHAR lpszBuf[1024];
|
|
|
|
ASSERT(NULL != pPasswordChallenge->u.password_in_the_clear.text_string);
|
|
|
|
WideCharToMultiByte( CP_ACP, 0,
|
|
pPasswordChallenge->u.password_in_the_clear.text_string,
|
|
-1,lpszBuf,256,NULL,NULL);
|
|
|
|
CHAR* lp = (CHAR *)_StrChr(lpszBuf, ':');
|
|
|
|
if (NULL == lp)
|
|
{
|
|
ERROR_OUT(("Expected separator in logon pwd"));
|
|
return FALSE;
|
|
}
|
|
|
|
*lp++ = '\0';
|
|
|
|
CHAR* lpPw = (CHAR *)_StrChr(lp, ':');
|
|
|
|
if (NULL == lpPw)
|
|
{
|
|
ERROR_OUT(("Expected 2nd separator in logon pwd"));
|
|
return FALSE;
|
|
}
|
|
|
|
*lpPw++ = '\0';
|
|
|
|
if (0 == strlen(lpPw))
|
|
{
|
|
WARNING_OUT(("Short password in logon pwd"));
|
|
return FALSE;
|
|
}
|
|
|
|
bSuccess = LogonUser(lpszBuf, lp, lpPw, LOGON32_LOGON_NETWORK,
|
|
LOGON32_PROVIDER_DEFAULT, &hToken);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
WARNING_OUT(("LogonUser failed %d", GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
if( !AllocateAndInitializeSid(&siaNtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS,
|
|
0,0,0,0,0,0, &psidAdministrators ))
|
|
{
|
|
ERROR_OUT(("Error getting admin group sid: %d", GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
// assume that we don't find the admin SID.
|
|
bSuccess = FALSE;
|
|
|
|
if (!CheckTokenMembership(hToken, psidAdministrators, &bSuccess))
|
|
{
|
|
ERROR_OUT(("Error checking token membership: %d", GetLastError()));
|
|
bSuccess = FALSE;
|
|
}
|
|
FreeSid(psidAdministrators);
|
|
|
|
//
|
|
// If this worked there is no need to go on
|
|
//
|
|
|
|
if ( bSuccess )
|
|
return TRUE;
|
|
//
|
|
// Check for group membership in the RDS users group on
|
|
// the local machine.
|
|
//
|
|
|
|
ASSERT(FALSE == bSuccess);
|
|
|
|
DWORD cbSid = 0;
|
|
DWORD cbDomain = 0;
|
|
SID_NAME_USE SidNameUse = SidTypeGroup;
|
|
|
|
if ( LookupAccountName ( NULL, SZRDSGROUP, NULL, &cbSid,
|
|
NULL, &cbDomain, &SidNameUse )
|
|
|| ERROR_INSUFFICIENT_BUFFER == GetLastError() )
|
|
{
|
|
PSID pSid = new BYTE[cbSid];
|
|
LPTSTR lpszDomain = new TCHAR[cbDomain];
|
|
|
|
if ( pSid && lpszDomain )
|
|
{
|
|
if ( LookupAccountName ( NULL, SZRDSGROUP, pSid,
|
|
&cbSid, lpszDomain, &cbDomain, &SidNameUse ))
|
|
{
|
|
//
|
|
// Make sure what we found is a group
|
|
//
|
|
|
|
if ( SidTypeGroup == SidNameUse ||
|
|
SidTypeAlias == SidNameUse )
|
|
{
|
|
if (!CheckTokenMembership(hToken, pSid, &bSuccess))
|
|
{
|
|
ERROR_OUT(("Error checking token membership: %d", GetLastError()));
|
|
bSuccess = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("SZRDSGROUP was not a group or alias? its a %d",
|
|
SidNameUse ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("LookupAccountName (2) failed: %d",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Alloc of sid or domain failed"));
|
|
}
|
|
|
|
delete pSid;
|
|
delete lpszDomain;
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("LookupAccountName (1) failed: %d", GetLastError()));
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
//
|
|
// We are going to hash the password and compare it to the
|
|
// stored hash
|
|
//
|
|
|
|
if (m_pbHashedPassword != NULL)
|
|
{
|
|
if (NULL != pPasswordChallenge->u.password_in_the_clear.text_string)
|
|
{
|
|
cbPasswordChallenge = hashObj.GetHashedData((LPBYTE)pPasswordChallenge->u.password_in_the_clear.text_string,
|
|
sizeof(WCHAR)*lstrlenW(pPasswordChallenge->u.password_in_the_clear.text_string),
|
|
(void **) &pbPasswordChallenge);
|
|
}
|
|
else if (NULL != pPasswordChallenge->u.password_in_the_clear.numeric_string)
|
|
{
|
|
int cch = lstrlenA((PSTR)pPasswordChallenge->u.password_in_the_clear.numeric_string);
|
|
LPWSTR lpwszNumPassword = new WCHAR[cch+1];
|
|
MultiByteToWideChar(CP_ACP, 0, (PSTR)pPasswordChallenge->u.password_in_the_clear.numeric_string,
|
|
-1, lpwszNumPassword, cch+1);
|
|
int cwch = lstrlenW(lpwszNumPassword);
|
|
cbPasswordChallenge = hashObj.GetHashedData((LPBYTE)lpwszNumPassword, sizeof(WCHAR)*lstrlenW(lpwszNumPassword), (void **) &pbPasswordChallenge);
|
|
delete []lpwszNumPassword;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_cbHashedPassword != cbPasswordChallenge) return FALSE;
|
|
if (0 == memcmp(m_pbHashedPassword, pbPasswordChallenge, cbPasswordChallenge))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (m_pwszPassword != NULL)
|
|
{
|
|
// We have a text password
|
|
if ((pPasswordChallenge->u.password_in_the_clear.text_string == NULL) ||
|
|
(0 != ::My_strcmpW(m_pwszPassword,
|
|
pPasswordChallenge->u.password_in_the_clear.text_string)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have a numeric password
|
|
if ((pPasswordChallenge->u.password_in_the_clear.numeric_string == NULL) ||
|
|
(::lstrcmpA(m_pszNumericPassword,
|
|
(PSTR) pPasswordChallenge->u.password_in_the_clear.numeric_string)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Join() - see erncconf.h */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConference::
|
|
Join
|
|
(
|
|
LPSTR pszNodeAddress,
|
|
PUSERDATAINFO pInfo,
|
|
UINT nInfo,
|
|
LPCWSTR _wszPassword
|
|
)
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
DebugEntry(DCRNCConference::Join);
|
|
|
|
/*
|
|
* Set the password that will be used by the JoinWrapper() method.
|
|
* The password will be deleted after the Join is complete.
|
|
* The m_pwszPassword member is only set for the top providers
|
|
* protecting conferences.
|
|
*/
|
|
if (! ::IsEmptyStringW (_wszPassword))
|
|
{
|
|
// Store the password; we will need it later
|
|
m_pwszPassword = ::My_strdupW(_wszPassword);
|
|
if (NULL == m_pwszPassword)
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SFR 6206. The apps treat joining a conference at a remote site as */
|
|
/* an "incoming" call. (i.e they discard any local data and accept the */
|
|
/* msgs/WB contents from the conference we are joining). */
|
|
/************************************************************************/
|
|
if (NO_ERROR == hr)
|
|
{
|
|
m_fIncoming = TRUE;
|
|
hr = StartConnection(pszNodeAddress,
|
|
CONF_CON_PENDING_JOIN,
|
|
pInfo,
|
|
nInfo,
|
|
m_fSecure);
|
|
}
|
|
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("Error starting connection"));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* We now sit and wait for the connection to complete before */
|
|
/* continuing. */
|
|
/************************************************************************/
|
|
DebugExitHRESULT(DCRNCConference::Join, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* NotifyConferenceComplete() - the generic conference has finished its */
|
|
/* attempt to start. */
|
|
/****************************************************************************/
|
|
void DCRNCConference::
|
|
NotifyConferenceComplete ( HRESULT hr )
|
|
{
|
|
DebugEntry(DCRNCConference::NotifyConferenceComplete);
|
|
|
|
/************************************************************************/
|
|
/* If the attempt fails, action depends on whether this is the first or */
|
|
/* second attempt. */
|
|
/************************************************************************/
|
|
if (NO_ERROR != hr)
|
|
{
|
|
TRACE_OUT(("Attempt to start failed"));
|
|
// LONCHANC: please do not remove this chunk of code.
|
|
#ifdef ENABLE_START_REMOTE
|
|
if (m_eState == CONF_ST_PENDING_START_REMOTE_FIRST)
|
|
{
|
|
TRACE_OUT(("Try second conference type"));
|
|
StartSecondConference(hr);
|
|
return;
|
|
}
|
|
#endif // ENABLE_START_REMOTE
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("Conference started OK."));
|
|
m_eState = CONF_ST_STARTED;
|
|
}
|
|
g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
|
|
|
|
DebugExitVOID(DCRNCConference::NotifyConferenceComplete);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* NotifyConnectionComplete() - see erncconf.h */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConference::
|
|
NotifyConnectionComplete
|
|
(
|
|
CLogicalConnection *pConEntry,
|
|
HRESULT hr
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConference::NotifyConnectionComplete);
|
|
|
|
// This function is the state machine
|
|
// for bringing up a conferencing protocol.
|
|
// It manages getting the physical connection and trying
|
|
// T120 and R1.1.
|
|
|
|
// A connection has started.
|
|
// Subsequent action depends on the pending state for the connection.
|
|
|
|
// First filter out internal (success) return codes.
|
|
if (NO_ERROR != hr)
|
|
{
|
|
// Failed to get a physical connection.
|
|
WARNING_OUT(("Failed to start connection"));
|
|
if (pConEntry->GetState() != CONF_CON_PENDING_INVITE)
|
|
{
|
|
|
|
// Put the connection in a failed state before notifying the user.
|
|
// This is because notifying the user can cause GCC events to fire,
|
|
// and, in particular, a JoinRequest failure which must be ignored.
|
|
|
|
pConEntry->SetState(CONF_CON_ERROR);
|
|
|
|
g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
|
|
goto MyExit;
|
|
}
|
|
// Drop through for invite failures.
|
|
}
|
|
|
|
switch (pConEntry->GetState())
|
|
{
|
|
// LONCHANC: please do not remove this chunk of code.
|
|
#ifdef ENABLE_START_REMOTE
|
|
case CONF_CON_PENDING_START:
|
|
/****************************************************************/
|
|
/* Check we are in the correct state. */
|
|
/****************************************************************/
|
|
if ( (m_eState != CONF_ST_PENDING_CONNECTION) &&
|
|
(m_eState != CONF_ST_LOCAL_PENDING_RECREATE))
|
|
{
|
|
ERROR_OUT(("Bad state to start in..."));
|
|
goto MyExit;
|
|
}
|
|
|
|
pConEntry->SetState(CONF_CON_CONNECTED);
|
|
|
|
/****************************************************************/
|
|
/* The connection has started OK. we now try to establish */
|
|
/* either a T120 or a backlevel conference, depending on the */
|
|
/* starting order. */
|
|
/****************************************************************/
|
|
if (NO_ERROR == hr)
|
|
{
|
|
hr = StartFirstConference();
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Invalid response in notify connection complete"));
|
|
}
|
|
break;
|
|
#endif // ENABLE_START_REMOTE
|
|
|
|
case CONF_CON_PENDING_JOIN:
|
|
// pConEntry->m_eState = CONF_CON_CONNECTED;
|
|
|
|
// Joining a new conference.
|
|
// Create a new generic conference and
|
|
// call its Join() entry point.
|
|
hr = NewT120Conference();
|
|
if (NO_ERROR == hr)
|
|
{
|
|
|
|
hr = JoinWrapper(pConEntry, m_pwszPassword);
|
|
// Delete the set password
|
|
if (m_pwszPassword != NULL)
|
|
{
|
|
delete m_pwszPassword;
|
|
m_pwszPassword = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Error %d joining conference", hr));
|
|
goto MyExit;
|
|
}
|
|
break;
|
|
|
|
case CONF_CON_PENDING_INVITE:
|
|
hr = pConEntry->InviteConnectResult(hr);
|
|
break;
|
|
|
|
default :
|
|
ERROR_OUT(("Unknown action %d", pConEntry->GetState()));
|
|
break;
|
|
}
|
|
|
|
MyExit:
|
|
DebugExitVOID(DCRNCConference::NotifyConnectionComplete);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT DCRNCConference::
|
|
JoinWrapper
|
|
(
|
|
CLogicalConnection *pConEntry,
|
|
LPCWSTR _wszPassword
|
|
)
|
|
{
|
|
DebugEntry(DCRNCConference::JoinWrapper);
|
|
|
|
// Going asynchronous, so allow events to fire.
|
|
pConEntry->ReArm();
|
|
|
|
HRESULT hr = T120Join(pConEntry->GetNodeAddress(),
|
|
pConEntry->IsConnectionSecure(),
|
|
m_pwszConfName,
|
|
pConEntry->GetUserDataList(),
|
|
_wszPassword);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
m_eState = CONF_ST_STARTED;
|
|
}
|
|
else
|
|
{
|
|
pConEntry->Grab();
|
|
ERROR_OUT(("Error %d joining conference", hr));
|
|
g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::JoinWrapper, hr);
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* NotifyRosterChanged() - see erncconf.hpp. */
|
|
/****************************************************************************/
|
|
void DCRNCConference::
|
|
NotifyRosterChanged ( PNC_ROSTER pRoster )
|
|
{
|
|
DebugEntry(DCRNCConference::NotifyRosterChanged);
|
|
|
|
// Add the conference name and ID to the roster.
|
|
pRoster->pwszConferenceName = m_pwszConfName;
|
|
pRoster->uConferenceID = m_nConfID;
|
|
|
|
/************************************************************************/
|
|
/* Pass the new roster up to the CM */
|
|
/************************************************************************/
|
|
g_pCallbackInterface->OnRosterChanged((CONF_HANDLE) this, pRoster);
|
|
|
|
DebugExitVOID(DCRNCConference::NotifyRosterChanged);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* StartConnection - add a new connection to our connection list. */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConference::
|
|
StartConnection
|
|
(
|
|
LPSTR pszNodeAddress,
|
|
LOGICAL_CONN_STATE eAction,
|
|
PUSERDATAINFO pInfo,
|
|
UINT nInfo,
|
|
BOOL fSecure,
|
|
REQUEST_HANDLE * phRequest
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CLogicalConnection *pConEntry;
|
|
|
|
DebugEntry(DCRNCConference::StartConnection);
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
pConEntry = NewLogicalConnection(eAction, NULL, pInfo, nInfo, fSecure);
|
|
if (NULL != pConEntry)
|
|
{
|
|
hr = NO_ERROR;
|
|
if (phRequest)
|
|
{
|
|
// Return context as the connection entry, if required.
|
|
*phRequest = (REQUEST_HANDLE *)pConEntry;
|
|
}
|
|
|
|
// Set node address
|
|
pConEntry->SetNodeAddress(::My_strdupA(pszNodeAddress));
|
|
|
|
//
|
|
// LONCHANC: Fire the conn-entry event.
|
|
//
|
|
hr = NotifyConnectionComplete(pConEntry, NO_ERROR);
|
|
}
|
|
else
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::StartConnection, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
// LONCHANC: please do not remove this chunk of code.
|
|
#ifdef ENABLE_START_REMOTE
|
|
/****************************************************************************/
|
|
/* StartFirstConference() - start the first attempt to create a conference. */
|
|
/****************************************************************************/
|
|
void DCRNCConference::
|
|
StartFirstConference(void)
|
|
{
|
|
BOOL result = FALSE;
|
|
HRESULT hr;
|
|
|
|
DebugEntry(DCRNCConference::StartFirstConference);
|
|
|
|
hr = NewT120Conference();
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("Failed to create new conference"));
|
|
m_eState = CONF_ST_UNINITIALIZED;
|
|
goto MyExit;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Call the StartRemote() entry point. */
|
|
/************************************************************************/
|
|
hr = T120StartRemote(m_pszFirstRemoteNodeAddress);
|
|
if (hr)
|
|
{
|
|
WARNING_OUT(("Failed to start remote, rc %d", hr));
|
|
goto MyExit;
|
|
}
|
|
m_eState = CONF_ST_PENDING_START_REMOTE_FIRST;
|
|
result = TRUE;
|
|
|
|
MyExit:
|
|
|
|
/************************************************************************/
|
|
/* If we failed to start the first conference, try to start the second */
|
|
/* type of conference in the starting order. */
|
|
/************************************************************************/
|
|
if (!result)
|
|
{
|
|
TRACE_OUT(("Failed to start first conference."));
|
|
StartSecondConference(hr);
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConference::StartFirstConference);
|
|
}
|
|
#endif // ENABLE_START_REMOTE
|
|
|
|
|
|
// LONCHANC: please do not remove this chunk of code.
|
|
#ifdef ENABLE_START_REMOTE
|
|
/****************************************************************************/
|
|
/* StartSecondConference() - start the second attempt to create a */
|
|
/* conference. */
|
|
/****************************************************************************/
|
|
void DCRNCConference::
|
|
StartSecondConference ( HRESULT FirstConferenceStatus )
|
|
{
|
|
BOOL result = FALSE;
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
DebugEntry(DCRNCConference::StartSecondConference);
|
|
|
|
hr = FirstConferenceStatus;
|
|
#if 0 // LONCHANC: very weird code
|
|
goto MyExit;
|
|
|
|
/************************************************************************/
|
|
/* Call the StartRemote() entry point. */
|
|
/************************************************************************/
|
|
hr = T120StartRemote(m_pszFirstRemoteNodeAddress);
|
|
if (NO_ERROR != hr)
|
|
{
|
|
WARNING_OUT(("Failed to start remote, rc %d", hr));
|
|
goto MyExit;
|
|
}
|
|
m_eState = CONF_ST_PENDING_START_REMOTE_SECOND;
|
|
result = TRUE;
|
|
|
|
MyExit:
|
|
#endif // 0
|
|
|
|
/************************************************************************/
|
|
/* If we have failed to start any type of conference, tell CM about it. */
|
|
/************************************************************************/
|
|
if (!result)
|
|
{
|
|
TRACE_OUT(("Failed to start Second conference."));
|
|
g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
|
|
}
|
|
|
|
DebugExitVOID(DCRNCConference::StartSecondConference);
|
|
}
|
|
#endif // ENABLE_START_REMOTE
|
|
|
|
|
|
/****************************************************************************/
|
|
/* StartLocal() - see erncconf.h */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConference::
|
|
StartLocal ( LPCWSTR _wszPassword, PBYTE pbHashedPassword, DWORD cbHashedPassword)
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
DebugEntry(DCRNCConference::StartLocal);
|
|
|
|
/*
|
|
* Set the password that will be used to protect the conference.
|
|
* against unauthorized Join requests.
|
|
* The password is only set for the top providers
|
|
* protecting conferences.
|
|
* If the password is a number it will be stored in m_pszNumericPassword.
|
|
* Otherwise, it will be stored in m_pwszPassword.
|
|
*/
|
|
if (NULL != pbHashedPassword)
|
|
{
|
|
m_pbHashedPassword = new BYTE[cbHashedPassword];
|
|
if (NULL == m_pbHashedPassword)
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
memcpy(m_pbHashedPassword, pbHashedPassword, cbHashedPassword);
|
|
m_cbHashedPassword = cbHashedPassword;
|
|
}
|
|
}
|
|
else if (! ::IsEmptyStringW(_wszPassword))
|
|
{
|
|
if (::UnicodeIsNumber(_wszPassword))
|
|
{
|
|
m_pszNumericPassword = ::UnicodeToAnsi(_wszPassword);
|
|
if (m_pszNumericPassword == NULL)
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pwszPassword = ::My_strdupW(_wszPassword);
|
|
if (NULL == m_pwszPassword)
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Dont need to bother getting a physical connection. Just create a */
|
|
/* new T120 conference and call its StartLocal() entry point */
|
|
/************************************************************************/
|
|
if (NO_ERROR == hr)
|
|
{
|
|
hr = NewT120Conference();
|
|
if (NO_ERROR == hr)
|
|
{
|
|
hr = T120StartLocal(m_fSecure);
|
|
if (NO_ERROR == hr)
|
|
{
|
|
m_eState = CONF_ST_PENDING_T120_START_LOCAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::StartLocal, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
// LONCHANC: please do not remove this chunk of code.
|
|
#ifdef ENABLE_START_REMOTE
|
|
/****************************************************************************/
|
|
/* StartRemote() - see erncconf.h */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConference::
|
|
StartRemote ( LPSTR pszNodeAddress )
|
|
{
|
|
HRESULT hr;
|
|
|
|
DebugEntry(DCRNCConference::StartRemote);
|
|
|
|
/************************************************************************/
|
|
/* Store the node details */
|
|
/************************************************************************/
|
|
m_pszFirstRemoteNodeAddress = ::My_strdupA(pszNodeAddress);
|
|
if (NULL != m_pszFirstRemoteNodeAddress)
|
|
{
|
|
/************************************************************************/
|
|
/* We need to set the conference state before trying to start a new */
|
|
/* connection - the connection may synchronously call us back and we */
|
|
/* want to be able to handle the callback correctly. */
|
|
/************************************************************************/
|
|
m_eState = CONF_ST_PENDING_CONNECTION;
|
|
|
|
/************************************************************************/
|
|
/* Start a new physical connection. */
|
|
/************************************************************************/
|
|
hr = StartConnection(m_pszFirstRemoteNodeAddress, CONF_CON_PENDING_START, NULL, NULL);
|
|
if (NO_ERROR != hr)
|
|
{
|
|
ERROR_OUT(("Error adding connection"));
|
|
m_eState = CONF_ST_UNINITIALIZED;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* We now sit and wait for the connection to complete before */
|
|
/* continuing. */
|
|
/************************************************************************/
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("DCRNCConference::StartRemote: can't duplicate node address"));
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
m_eState = CONF_ST_UNINITIALIZED;
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::StartRemote, hr);
|
|
return hr;
|
|
}
|
|
#endif // ENABLE_START_REMOTE
|
|
|
|
|
|
/****************************************************************************/
|
|
/* StartIncoming() - see erncconf.h */
|
|
/****************************************************************************/
|
|
HRESULT DCRNCConference::
|
|
StartIncoming(void)
|
|
{
|
|
DebugEntry(DCRNCConference::StartIncoming);
|
|
|
|
/************************************************************************/
|
|
/* Set the incoming flag. */
|
|
/************************************************************************/
|
|
m_fIncoming = TRUE;
|
|
|
|
/************************************************************************/
|
|
/* Create a new T120 conference and call its StartIncoming entry point. */
|
|
/************************************************************************/
|
|
HRESULT hr = NewT120Conference();
|
|
if (NO_ERROR == hr)
|
|
{
|
|
m_eState = CONF_ST_STARTED;
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("Failed to create new local conference"));
|
|
}
|
|
|
|
DebugExitHRESULT(DCRNCConference::StartIncoming, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
CLogicalConnection::
|
|
CLogicalConnection
|
|
(
|
|
PCONFERENCE pConf,
|
|
LOGICAL_CONN_STATE eAction,
|
|
ConnectionHandle hConnection,
|
|
PUSERDATAINFO pInfo,
|
|
UINT nInfo,
|
|
BOOL fSecure
|
|
)
|
|
:
|
|
CRefCount(MAKE_STAMP_ID('C','L','N','E')),
|
|
m_pszNodeAddress(NULL),
|
|
m_eState(eAction),
|
|
m_pConf(pConf),
|
|
m_nidConnection(0),
|
|
m_hInviteReqConn(hConnection),
|
|
m_hConnection(hConnection),
|
|
m_pLocalAddress(NULL),
|
|
m_fEventGrabbed(FALSE),
|
|
m_fSecure(fSecure)
|
|
{
|
|
DebugEntry(CLogicalConnection::CLogicalConnection);
|
|
|
|
if(nInfo)
|
|
{
|
|
for (UINT i = 0 ; i < nInfo; i++, pInfo++)
|
|
{
|
|
m_UserDataInfoList.AddUserData(pInfo->pGUID, pInfo->cbData, pInfo->pData);
|
|
}
|
|
}
|
|
|
|
if ((eAction == CONF_CON_INVITED) ||
|
|
(eAction == CONF_CON_JOINED))
|
|
{
|
|
Grab(); // No events to fire.
|
|
}
|
|
|
|
DebugExitVOID(CLogicalConnection::CLogicalConnection);
|
|
}
|
|
|
|
|
|
CLogicalConnection::
|
|
~CLogicalConnection(void)
|
|
{
|
|
DebugEntry(CLogicalConnection::~CLogicalConnection);
|
|
|
|
ASSERT((m_eState == CONF_CON_CONNECTED) ||
|
|
(m_eState == CONF_CON_ERROR));
|
|
|
|
delete m_pszNodeAddress;
|
|
|
|
DebugExitVOID(CLogicalConnection::~CLogicalConnection);
|
|
}
|
|
|
|
|
|
BOOL CLogicalConnection::
|
|
NewLocalAddress(void)
|
|
{
|
|
BOOL bNewAddress;
|
|
m_pConf->AddLocalAddress(m_hConnection, &bNewAddress, &m_pLocalAddress);
|
|
return bNewAddress;
|
|
}
|
|
|
|
|
|
HRESULT CLogicalConnection::
|
|
InviteConnectResult ( HRESULT hr )
|
|
{
|
|
DebugEntry(CLogicalConnection::InviteConnectResult);
|
|
|
|
if (NO_ERROR == hr)
|
|
{
|
|
/****************************************************************/
|
|
/* Check the state - we should be fully initialized and have a */
|
|
/* generic conference by this stage. */
|
|
/****************************************************************/
|
|
if (m_pConf->m_eState != CONF_ST_STARTED)
|
|
{
|
|
ERROR_OUT(("Bad state %d", m_pConf->m_eState));
|
|
hr = UI_NO_SUCH_CONFERENCE;
|
|
}
|
|
else
|
|
{
|
|
// Now have a connection to the conference, so go do invite.
|
|
// Note that this may not be the only invite if the user invites
|
|
// several people into the conference before the connection is up.
|
|
ReArm(); // So that connection going down fires off event handling
|
|
hr = m_pConf->T120Invite(m_pszNodeAddress,
|
|
m_fSecure,
|
|
&m_UserDataInfoList,
|
|
&m_hInviteReqConn);
|
|
if (NO_ERROR != hr)
|
|
{
|
|
Grab();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NO_ERROR != hr)
|
|
{
|
|
InviteComplete(hr);
|
|
}
|
|
|
|
DebugExitHRESULT(CLogicalConnection::InviteConnectResult, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
void DCRNCConference::
|
|
InviteComplete
|
|
(
|
|
ConnectionHandle hInviteReqConn,
|
|
HRESULT result,
|
|
PT120PRODUCTVERSION pVersion
|
|
)
|
|
{
|
|
CLogicalConnection * pConEntry;
|
|
|
|
DebugEntry(DCRNCConference::InviteComplete);
|
|
|
|
pConEntry = GetConEntry(hInviteReqConn);
|
|
if (pConEntry == NULL)
|
|
{
|
|
ERROR_OUT(("Unable to match invite response with request"));
|
|
return;
|
|
}
|
|
pConEntry->SetConnectionHandle(hInviteReqConn);
|
|
pConEntry->InviteComplete(result, pVersion);
|
|
|
|
DebugExitVOID(DCRNCConference::InviteComplete);
|
|
}
|
|
|
|
|
|
HRESULT CLocalAddressList::
|
|
AddLocalAddress
|
|
(
|
|
ConnectionHandle connection_handle,
|
|
BOOL *pbNewAddress,
|
|
CLocalAddress **ppLocalAddrToRet
|
|
)
|
|
{
|
|
HRESULT hr = UI_RC_OUT_OF_MEMORY;
|
|
CLocalAddress * pLocalAddress = NULL;
|
|
char szLocalAddress[64];
|
|
int nLocalAddress = sizeof(szLocalAddress);
|
|
|
|
DebugEntry(CLocalAddressList::AddLocalAddress);
|
|
|
|
*pbNewAddress = FALSE;
|
|
ASSERT (g_pMCSController != NULL);
|
|
if (g_pMCSController->GetLocalAddress (connection_handle, szLocalAddress,
|
|
&nLocalAddress)) {
|
|
DBG_SAVE_FILE_LINE
|
|
pLocalAddress = new CLocalAddress(szLocalAddress);
|
|
if (pLocalAddress) {
|
|
if (!IS_EMPTY_STRING(pLocalAddress->m_pszLocalAddress)) {
|
|
BOOL fFound = FALSE;
|
|
CLocalAddress *p;
|
|
Reset();
|
|
while (NULL != (p = Iterate()))
|
|
{
|
|
if (0 == ::lstrcmpA(p->m_pszLocalAddress, szLocalAddress))
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! fFound)
|
|
{
|
|
ASSERT(NULL == p);
|
|
Append(pLocalAddress);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(NULL != p);
|
|
pLocalAddress->Release();
|
|
(pLocalAddress = p)->AddRef();
|
|
}
|
|
hr = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pLocalAddress->Release(); // Remove when no longer referenced
|
|
pLocalAddress = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppLocalAddrToRet = pLocalAddress;
|
|
|
|
DebugExitHRESULT(CLocalAddressList::AddLocalAddress, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLocalAddressList::
|
|
GetLocalAddressList
|
|
(
|
|
UINT *pnAddresses,
|
|
LPCSTR **ppaAddresses
|
|
)
|
|
{
|
|
CLocalAddress * pAddress;
|
|
LPCSTR * pConnection;
|
|
LPCSTR * apConn = NULL;
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
DebugEntry(CLocalAddressList::GetLocalAddressList);
|
|
|
|
if (! IsEmpty())
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
if (NULL == (apConn = new LPCSTR[GetCount()]))
|
|
{
|
|
hr = UI_RC_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (NULL == apConn)
|
|
{
|
|
*pnAddresses = 0;
|
|
}
|
|
else
|
|
{
|
|
hr = NO_ERROR;
|
|
*pnAddresses = GetCount();
|
|
pConnection = apConn;
|
|
|
|
Reset();
|
|
while (NULL != (pAddress = Iterate()))
|
|
{
|
|
*pConnection++ = pAddress->m_pszLocalAddress;
|
|
}
|
|
}
|
|
*ppaAddresses = apConn;
|
|
|
|
DebugExitHRESULT(CLocalAddressList::GetLocalAddressList, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CLocalAddressList::
|
|
EndReference ( CLocalAddress *pLocalAddress )
|
|
{
|
|
DebugEntry(CLocalAddressList::EndReference);
|
|
|
|
if (pLocalAddress->Release() == 0)
|
|
{
|
|
Remove(pLocalAddress);
|
|
}
|
|
|
|
DebugExitVOID(CLocalAddressList::EndReference);
|
|
}
|
|
|
|
|
|
|
|
CLocalAddress::CLocalAddress(PCSTR szLocalAddress)
|
|
:
|
|
CRefCount(MAKE_STAMP_ID('L','A','D','R'))
|
|
{
|
|
m_pszLocalAddress = ::My_strdupA(szLocalAddress);
|
|
}
|
|
|
|
|
|
void CLogicalConnection::
|
|
InviteComplete
|
|
(
|
|
HRESULT hrStatus,
|
|
PT120PRODUCTVERSION pVersion
|
|
)
|
|
{
|
|
DebugEntry(CLogicalConnection::InviteComplete);
|
|
|
|
// Don't want user calling us back in
|
|
// InviteConferenceResult to delete the conference
|
|
// causing this object to be deleted whilst
|
|
// in it.
|
|
AddRef();
|
|
|
|
// Should only handle an invite complete if there is one pending.
|
|
// Otherwise, this is most likely the result of entering this function
|
|
// after telling the user that the invite has failed for some other
|
|
// reason (e.g. a physical connection going down).
|
|
// In these cases, just ignore the invite complete event.
|
|
|
|
if (m_eState == CONF_CON_PENDING_INVITE)
|
|
{
|
|
// Invite complete will generate an event, so grab it.
|
|
|
|
Grab();
|
|
|
|
if (hrStatus)
|
|
{
|
|
m_eState = CONF_CON_ERROR;
|
|
}
|
|
else
|
|
{
|
|
m_eState = CONF_CON_CONNECTED;
|
|
if (NewLocalAddress())
|
|
{
|
|
m_pConf->AnnouncePresence();
|
|
}
|
|
}
|
|
g_pCallbackInterface->OnInviteResult(
|
|
(CONF_HANDLE) m_pConf,
|
|
(REQUEST_HANDLE) this,
|
|
m_nidConnection,
|
|
hrStatus,
|
|
pVersion);
|
|
if (hrStatus)
|
|
{
|
|
// Remove conentry from conference if invite fails.
|
|
Delete(hrStatus);
|
|
}
|
|
}
|
|
|
|
Release();
|
|
|
|
DebugExitVOID(CLogicalConnection::InviteComplete);
|
|
}
|
|
|
|
|
|
void CLogicalConnection::
|
|
Delete ( HRESULT hrReason )
|
|
{
|
|
DebugEntry(CLogicalConnection::Delete);
|
|
|
|
// WARNING, WARNING, WARNING:
|
|
// This method gets re-entered on the stack.
|
|
// Note guards in code below.
|
|
if (NULL != m_pConf)
|
|
{
|
|
PCONFERENCE pThisConf = m_pConf;
|
|
PCONFERENCE pConfToFree = NULL;
|
|
m_pConf = NULL;
|
|
|
|
// The connection is going away, so remove the reference to the
|
|
// associated local connection (if any).
|
|
if (NULL != m_pLocalAddress)
|
|
{
|
|
pThisConf->EndReference(m_pLocalAddress);
|
|
m_pLocalAddress = NULL;
|
|
}
|
|
|
|
if (m_eState == CONF_CON_INVITED)
|
|
{
|
|
// The conference associated with the entry was invited into the conference,
|
|
// so remove the conference and all of its connections.
|
|
|
|
m_eState = CONF_CON_ERROR; // Only do this once
|
|
pConfToFree = pThisConf;
|
|
}
|
|
|
|
// If there is a pending event on the connection,
|
|
// then try to grab it and notify the requestor
|
|
// that the request has failed.
|
|
// Note that the event handler may itself end up
|
|
// recalling this function, so it refcounts the
|
|
// CLogicalConnection to prevent it from being destructed
|
|
// too soon.
|
|
|
|
if (Grab())
|
|
{
|
|
pThisConf->NotifyConnectionComplete(this, hrReason);
|
|
}
|
|
|
|
// Set the connection state to be in error.
|
|
// Note that this is done after firing the event because
|
|
// otherwise a failed connection attempt to a disabled transport
|
|
// would cause the local conference to be destroyed by
|
|
// NotifyConnectionComplete().
|
|
m_eState = CONF_CON_ERROR;
|
|
|
|
// Sever the connection entry with the conference record.
|
|
pThisConf->m_ConnList.Remove(this);
|
|
|
|
// Now destroy the conentry once any pending event has fired -
|
|
// there is only a pending connection request or a pending
|
|
// request to join/invite/create a conference, and never both.
|
|
Release();
|
|
|
|
if (NULL != pConfToFree)
|
|
{
|
|
g_pNCConfMgr->RemoveConference(pConfToFree);
|
|
}
|
|
}
|
|
|
|
DebugExitVOID(CLogicalConnection::Delete);
|
|
}
|
|
|
|
|
|
BOOL FindSocketNumber(DWORD nid, SOCKET * socket_number)
|
|
{
|
|
(*socket_number) = 0;
|
|
ASSERT(g_pNCConfMgr != NULL);
|
|
|
|
return g_pNCConfMgr->FindSocketNumber((GCCNodeID) nid, socket_number);
|
|
}
|
|
|
|
// DCRNCConference::FindSocketNumber
|
|
// Given a GCCNodeID, finds a socket number associated with that id.
|
|
// Returns TRUE if we are directly connected topology-wise to the node, FALSE if not.
|
|
BOOL DCRNCConference::
|
|
FindSocketNumber
|
|
(
|
|
GCCNodeID nid,
|
|
SOCKET *socket_number
|
|
)
|
|
{
|
|
CLogicalConnection *pConEntry;
|
|
m_ConnList.Reset();
|
|
while (NULL != (pConEntry = m_ConnList.Iterate()))
|
|
{
|
|
if (pConEntry->GetConnectionNodeID() == nid)
|
|
{
|
|
// Found it!
|
|
g_pMCSController->FindSocketNumber(pConEntry->GetConnectionHandle(), socket_number);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
ULONG DCRNCConference::
|
|
GetNodeName(GCCNodeID NodeId, LPSTR pszBuffer, ULONG cbBufSize)
|
|
{
|
|
LPSTR pszName = m_NodeIdNameList.Find(NodeId);
|
|
if (pszName)
|
|
{
|
|
::lstrcpynA(pszBuffer, pszName, cbBufSize);
|
|
return lstrlenA(pszName);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
ULONG DCRNCConference::
|
|
GetUserGUIDData(GCCNodeID NodeId, GUID *pGuid,
|
|
LPBYTE pbBuffer, ULONG cbBufSize)
|
|
{
|
|
CNCUserDataList *pUserDataList = m_UserDataList.Find(NodeId);
|
|
GCCUserData *pUserData;
|
|
|
|
if (pUserDataList)
|
|
{
|
|
pUserData = pUserDataList->GetUserGUIDData(pGuid);
|
|
if (pUserData)
|
|
{
|
|
if (pbBuffer)
|
|
{
|
|
::CopyMemory(pbBuffer, pUserData->octet_string->value + sizeof(GUID),
|
|
min(cbBufSize, pUserData->octet_string->length - sizeof(GUID)));
|
|
}
|
|
return pUserData->octet_string->length - sizeof(GUID);
|
|
}
|
|
// The GUID is not found
|
|
}
|
|
// The NodeId is not found
|
|
return 0;
|
|
}
|
|
|
|
|
|
void DCRNCConference::
|
|
UpdateNodeIdNameListAndUserData(PGCCMessage message)
|
|
{
|
|
GCCNodeID NodeId;
|
|
LPSTR pszName;
|
|
LPWSTR pwszNodeName;
|
|
GCCNodeRecord *pNodeRecord;
|
|
PGCCConferenceRoster pConfRost;
|
|
USHORT count;
|
|
|
|
PGCCUserData pGccUserData;
|
|
USHORT count2;
|
|
CNCUserDataList *pUserDataList;
|
|
|
|
ASSERT (message->message_type == GCC_ROSTER_REPORT_INDICATION);
|
|
|
|
pConfRost = message->u.conf_roster_report_indication.conference_roster;
|
|
|
|
for (count = 0; count < pConfRost->number_of_records; count++)
|
|
{
|
|
pNodeRecord = pConfRost->node_record_list[count];
|
|
NodeId = pNodeRecord->node_id;
|
|
pwszNodeName = pNodeRecord->node_name;
|
|
|
|
pszName = m_NodeIdNameList.Find(NodeId);
|
|
if (!pszName)
|
|
{
|
|
int ccnsize = (lstrlenW(pwszNodeName) + 1) * sizeof(WCHAR);
|
|
DBG_SAVE_FILE_LINE
|
|
pszName = new char[ccnsize];
|
|
if (pszName)
|
|
{
|
|
if (WideCharToMultiByte(CP_ACP, 0, pwszNodeName, -1, pszName, ccnsize, NULL, NULL))
|
|
{
|
|
m_NodeIdNameList.Append(NodeId, pszName);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("ConfMgr::UpdateNodeIdNameList: cannot convert unicode node name"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("ConfMgr::UpdateNodeIdNameList: cannot duplicate unicode node name"));
|
|
}
|
|
}
|
|
|
|
|
|
for (count2 = 0; count2 < pNodeRecord->number_of_user_data_members; count2++)
|
|
{
|
|
pGccUserData = pNodeRecord->user_data_list[count2];
|
|
if (pGccUserData->octet_string->length <= sizeof(GUID))
|
|
continue; // not real user data
|
|
|
|
pUserDataList = m_UserDataList.Find(NodeId);
|
|
if (!pUserDataList)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
pUserDataList = new CNCUserDataList;
|
|
m_UserDataList.Append(NodeId, pUserDataList);
|
|
}
|
|
|
|
pUserDataList->AddUserData((GUID *)pGccUserData->octet_string->value,
|
|
pGccUserData->octet_string->length - sizeof(GUID),
|
|
pGccUserData->octet_string->value + sizeof(GUID));
|
|
|
|
}
|
|
}
|
|
}
|
|
|