Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2181 lines
64 KiB

/****************************************************************************/
/* */
/* 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 "plgxprt.h"
#include <service.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) 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,
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,
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
0,
NULL,
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;
UINT x;
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;
}
bSuccess = GetTokenInformation(hToken, TokenGroups, ptgGroups,
1024, &dwInfoBufferSize);
if (!bSuccess)
{
ERROR_OUT(("GetTokenInformation 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;
for ( x=0; x < ptgGroups->GroupCount; x++ )
{
if( EqualSid(psidAdministrators, ptgGroups->Groups[x].Sid) )
{
bSuccess = TRUE;
break;
}
}
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 )
{
for ( x=0; x < ptgGroups->GroupCount; x++ )
{
if( EqualSid(pSid, ptgGroups->Groups[x].Sid) )
{
bSuccess = TRUE;
break;
}
}
}
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,
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,
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,
BOOL fSecure,
REQUEST_HANDLE * phRequest
)
{
HRESULT hr;
CLogicalConnection *pConEntry;
DebugEntry(DCRNCConference::StartConnection);
DBG_SAVE_FILE_LINE
pConEntry = NewLogicalConnection(eAction, NULL, 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,
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 ((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
)
{
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);
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
)
{
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);
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));
}
}
}