WindowsXP-SP1/enduser/netmeeting/core/iconf.cpp

3212 lines
77 KiB
C++

// File: iconf.cpp
#include "precomp.h"
#include "version.h"
#include "ichnlaud.h"
#include "ichnlvid.h"
#include "ichnldat.h"
#include "rostinfo.h"
#include "imanager.h"
#include "isysinfo.h"
#include "imstream.h"
#include "medialst.h"
#include <tsecctrl.h>
typedef CEnumNmX<IEnumNmChannel, &IID_IEnumNmChannel, INmChannel, INmChannel> CEnumNmChannel;
// BUGBUG:
// This is defined as 128 because the RNC_ROSTER structure has the
// same limitation. Investigate what the appropriate number is.
const int MAX_CALLER_NAME = 128;
static const WCHAR _szConferenceNameDefault[] = L"Personal Conference";
static HRESULT OnNotifyStateChanged(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyMemberAdded(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyMemberUpdated(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyMemberRemoved(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyChannelAdded(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyChannelUpdated(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyChannelRemoved(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyNmUI(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyStreamEvent(IUnknown *pConfNotify, PVOID pv, REFIID riid);
static DWORD PF_VER_FromDw(DWORD dw);
static DWORD PF_VER_FromUserData(ROSTER_DATA_HANDLE hUserData);
static const IID * g_apiidCP[] =
{
{&IID_INmConferenceNotify},
{&IID_INmConferenceNotify2}
};
struct StreamEventInfo
{
INmChannel *pChannel;
NM_STREAMEVENT uEventCode;
UINT uSubCode;
};
class CUserDataOut
{
private:
int m_nEntries;
PUSERDATAINFO m_pudi;
CRosterInfo m_ri;
PBYTE m_pbSecurity;
public:
CUserDataOut(BOOL fSecure, BSTR bstrUserString);
~CUserDataOut()
{
delete [] m_pbSecurity;
delete [] m_pudi;
}
PUSERDATAINFO Data() { return m_pudi; }
int Entries() { return m_nEntries; }
};
CUserDataOut::CUserDataOut(BOOL fSecure, BSTR bstrUserString) :
m_nEntries(0),
m_pudi(NULL),
m_pbSecurity(NULL)
{
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
if (NULL != pOprahNCUI)
{
BOOL fULSNameValid = FALSE;
ULONG cbSecurity = 0;
ULONG cbUserString = 0;
if (fULSNameValid = pOprahNCUI->GetULSName(&m_ri))
m_nEntries++;
DWORD dwResult;
if ( fSecure )
{
if (cbSecurity =
pOprahNCUI->GetAuthenticatedName(&m_pbSecurity)) {
m_nEntries++;
}
}
if (bstrUserString)
{
if (cbUserString = SysStringByteLen(bstrUserString))
{
m_nEntries++;
}
}
// only add the LocalNodeId to the call user data if H323 is enabled
if (pOprahNCUI->IsH323Enabled())
{
m_nEntries++;
}
m_pudi = new USERDATAINFO[m_nEntries];
if (m_pudi != NULL)
{
m_nEntries = 0;
if (fULSNameValid)
{
m_pudi[m_nEntries].pData = NULL;
m_pudi[m_nEntries].pGUID = (PGUID) &g_csguidRostInfo;
m_ri.Save(&(m_pudi[m_nEntries].pData), &(m_pudi[m_nEntries].cbData));
m_nEntries++;
}
if (cbSecurity > 0) {
m_pudi[m_nEntries].pData = m_pbSecurity;
m_pudi[m_nEntries].cbData = cbSecurity;
m_pudi[m_nEntries].pGUID = (PGUID) &g_csguidSecurity;
m_nEntries++;
}
if (cbUserString > 0) {
m_pudi[m_nEntries].pData = bstrUserString;
m_pudi[m_nEntries].cbData = cbUserString;
m_pudi[m_nEntries].pGUID = (PGUID) &g_csguidUserString;
m_nEntries++;
}
// only add the LocalNodeId to the call user data if H323 is enabled
if (pOprahNCUI->IsH323Enabled())
{
m_pudi[m_nEntries].pData = &g_guidLocalNodeId;
m_pudi[m_nEntries].cbData = sizeof(g_guidLocalNodeId);
m_pudi[m_nEntries].pGUID = (PGUID) &g_csguidNodeIdTag;
m_nEntries++;
}
}
}
}
CConfObject::CConfObject() :
CConnectionPointContainer(g_apiidCP, ARRAY_ELEMENTS(g_apiidCP)),
m_hConf (NULL),
m_csState (CS_UNINITIALIZED),
m_fConferenceCreated(FALSE),
m_bstrConfName (NULL),
m_bstrConfPassword (NULL),
m_pbConfHashedPassword (NULL),
m_cbConfHashedPassword (0),
m_fServerMode (FALSE),
m_uDataMembers (0),
m_uMembers (0),
m_uH323Endpoints (0),
m_ourNodeID (0),
m_pMemberLocal (NULL),
m_uGCCConferenceID (0),
m_pChannelAudioLocal(NULL),
m_pChannelVideoLocal(NULL),
m_pChannelAudioRemote(NULL),
m_pChannelVideoRemote(NULL),
m_fSecure (FALSE),
m_attendeePermissions (NM_PERMIT_ALL),
m_maxParticipants (-1),
m_cRef (1)
{
DebugEntry(CConfObject::CConfObject);
DebugExitVOID(CConfObject::CConfObject);
}
CConfObject::~CConfObject()
{
DebugEntry(CConfObject::~CConfObject);
// Empty the participant list:
while (!m_MemberList.IsEmpty())
{
CNmMember * pMember = (CNmMember *) m_MemberList.RemoveHead();
// Shouldn't have any NULL entries:
ASSERT(pMember);
pMember->Release();
}
_EraseDataChannelGUIDS();
SysFreeString(m_bstrConfName);
SysFreeString(m_bstrConfPassword);
delete []m_pbConfHashedPassword;
DebugExitVOID(CConfObject::~CConfObject);
}
VOID CConfObject::SetConfName(BSTR bstr)
{
SysFreeString(m_bstrConfName);
m_bstrConfName = SysAllocString(bstr);
}
VOID CConfObject::SetConfPassword(BSTR bstr)
{
ASSERT (NULL == m_pbConfHashedPassword);
SysFreeString(m_bstrConfPassword);
m_bstrConfPassword = SysAllocString(bstr);
}
VOID CConfObject::SetConfHashedPassword(BSTR bstr)
{
int cch = 0;
ASSERT (NULL == m_bstrConfPassword);
delete []m_pbConfHashedPassword;
m_pbConfHashedPassword = NULL;
if (NULL == bstr) return;
cch = SysStringByteLen(bstr);
m_pbConfHashedPassword = (PBYTE) new BYTE[cch];
if (NULL == m_pbConfHashedPassword) {
ERROR_OUT(("CConfObject::SetConfHashedPassword() - Out of merory."));
return;
}
memcpy(m_pbConfHashedPassword, bstr, cch);
m_cbConfHashedPassword = cch;
}
VOID CConfObject::SetConfSecurity(BOOL fSecure)
{
NM_CONFERENCE_STATE NmState;
m_fSecure = fSecure;
// Force update of the status icon to reflect security
GetState(&NmState);
NotifySink((PVOID) NmState, OnNotifyStateChanged);
}
VOID CConfObject::SetConfAttendeePermissions(NM30_MTG_PERMISSIONS attendeePermissions)
{
m_attendeePermissions = attendeePermissions;
}
VOID CConfObject::SetConfMaxParticipants(UINT maxParticipants)
{
m_maxParticipants = maxParticipants;
}
HRESULT CConfObject::CreateConference(void)
{
DebugEntry(CConfObject::CreateConference);
HRESULT nsRet = E_FAIL;
switch (m_csState)
{
case CS_UNINITIALIZED:
case CS_TERMINATED:
{
if ((NULL == m_bstrConfName) || (0 == *m_bstrConfName))
{
m_bstrConfName = SysAllocString(_szConferenceNameDefault);
}
TRACE_OUT(("CConfObject:CreateConference [%ls]", m_bstrConfName));
ASSERT(g_pNodeController);
ASSERT(NULL == m_hConf);
nsRet = g_pNodeController->CreateConference(
m_bstrConfName,
m_bstrConfPassword,
m_pbConfHashedPassword,
m_cbConfHashedPassword,
m_fSecure,
&m_hConf);
if (0 == nsRet)
{
SetT120State(CS_CREATING);
}
else
{
m_hConf = NULL;
}
break;
}
default:
{
WARNING_OUT(("CConfObject: Can't create - bad state"));
nsRet = E_FAIL;
}
}
DebugExitINT(CConfObject::CreateConference, nsRet);
return nsRet;
}
HRESULT CConfObject::JoinConference( LPCWSTR pcwszConferenceName,
LPCWSTR pcwszPassword,
LPCSTR pcszAddress,
BSTR bstrUserString,
BOOL fRetry)
{
DebugEntry(CConfObject::JoinConference);
HRESULT nsRet = E_FAIL;
switch (m_csState)
{
case CS_COMING_UP:
{
if (!fRetry)
{
break;
}
// fall through if this is another attempt to join
}
case CS_UNINITIALIZED:
case CS_TERMINATED:
{
TRACE_OUT(("CConfObject: Joining conference..."));
CUserDataOut userData(m_fSecure, bstrUserString);
ASSERT(g_pNodeController);
nsRet = g_pNodeController->JoinConference(pcwszConferenceName,
pcwszPassword,
pcszAddress,
m_fSecure,
userData.Data(),
userData.Entries(),
&m_hConf);
if (0 == nsRet)
{
SetT120State(CS_COMING_UP);
}
else
{
m_hConf = NULL;
}
break;
}
case CS_GOING_DOWN:
default:
{
WARNING_OUT(("CConfObject: Can't join - bad state"));
// BUGBUG: define return values
nsRet = 1;
}
}
DebugExitINT(CConfObject::JoinConference, nsRet);
return nsRet;
}
HRESULT CConfObject::InviteConference( LPCSTR Address,
BSTR bstrUserString,
REQUEST_HANDLE *phRequest )
{
DebugEntry(CConfObject::InviteConference);
HRESULT nsRet = E_FAIL;
ASSERT(phRequest);
switch (m_csState)
{
case CS_RUNNING:
{
TRACE_OUT(("CConfObject: Inviting conference..."));
CUserDataOut userData(m_fSecure, bstrUserString);
ASSERT(g_pNodeController);
ASSERT(m_hConf);
m_hConf->SetSecurity(m_fSecure);
nsRet = m_hConf->Invite(Address,
userData.Data(),
userData.Entries(),
phRequest);
break;
}
default:
{
WARNING_OUT(("CConfObject: Can't invite - bad state"));
nsRet = E_FAIL;
}
}
DebugExitINT(CConfObject::InviteConference, nsRet);
return nsRet;
}
HRESULT CConfObject::LeaveConference(BOOL fForceLeave)
{
DebugEntry(CConfObject::LeaveConference);
HRESULT nsRet = E_FAIL;
REQUEST_HANDLE hReq = NULL;
switch (m_csState)
{
case CS_GOING_DOWN:
{
// we're already going down
nsRet = S_OK;
break;
}
case CS_COMING_UP:
case CS_RUNNING:
{
if (FALSE == fForceLeave)
{
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
if (NULL != pOprahNCUI)
{
int nNodes = pOprahNCUI->GetOutgoingCallCount();
if (m_fServerMode || (nNodes > 1) || (m_uDataMembers > 1))
{
// We are either in the process of calling another node
// or we have other people in our conference roster
TRACE_OUT(("CConfObject: Not leaving (there are other nodes)"));
break;
}
}
}
TRACE_OUT(("CConfObject: Leaving conference..."));
ASSERT(g_pNodeController);
ASSERT(m_hConf);
SetT120State(CS_GOING_DOWN);
nsRet = m_hConf->Leave();
break;
}
default:
{
WARNING_OUT(("CConfObject: Can't leave - bad state"));
break;
}
}
DebugExitINT(CConfObject::LeaveConference, nsRet);
return nsRet;
}
BOOL CConfObject::OnT120Invite(CONF_HANDLE hConference, BOOL fSecure)
{
DebugEntry(CConfObject::OnT120Invite);
BOOL bRet = FALSE;
switch (m_csState)
{
case CS_UNINITIALIZED:
case CS_TERMINATED:
{
TRACE_OUT(("CConfObject: Accepting a conference invitation..."));
ASSERT(g_pNodeController);
ASSERT(NULL == m_hConf);
m_hConf = hConference;
m_fSecure = fSecure;
hConference->SetSecurity(m_fSecure);
// WORKITEM need to issue INmManagerNotify::ConferenceCreated()
SetT120State(CS_COMING_UP);
bRet = TRUE;
break;
}
default:
{
WARNING_OUT(("CConfObject: Can't accept invite - bad state"));
}
}
DebugExitBOOL(CConfObject::OnT120Invite, bRet);
return bRet;
}
BOOL CConfObject::OnRosterChanged(PNC_ROSTER pRoster)
{
DebugEntry(CConfObject::OnRosterChanged);
BOOL bRet = TRUE;
int i;
// REVIEW: Could these be done more efficiently?
if (NULL != pRoster)
{
#ifdef DEBUG
TRACE_OUT(("Data Roster Dump: for conference ID = %d", pRoster->uConferenceID));
for (i = 0; i < (int) pRoster->uNumNodes; i++)
{
TRACE_OUT(( "\tID:%d\tName:%ls",
pRoster->nodes[i].uNodeID,
pRoster->nodes[i].pwszNodeName));
ASSERT(g_pNodeController);
UINT cbData;
PVOID pData;
if (NOERROR == g_pNodeController->GetUserData(
pRoster->nodes[i].hUserData,
(GUID*) &g_csguidRostInfo,
&cbData,
&pData))
{
CRosterInfo ri;
ri.Load(pData);
ri.Dump();
}
}
#endif // DEBUG
UINT nExistingParts = 0;
// Allocate an array of markers:
UINT uRosterNodes = pRoster->uNumNodes;
LPBOOL pMarkArray = new BOOL[uRosterNodes];
m_ourNodeID = pRoster->uLocalNodeID;
m_uGCCConferenceID = pRoster->uConferenceID;
if (NULL != pRoster->pwszConferenceName)
{
SysFreeString(m_bstrConfName);
m_bstrConfName = SysAllocString(pRoster->pwszConferenceName);
}
if (NULL != pMarkArray)
{
// Zero out the array:
for (UINT i = 0; i < uRosterNodes; i++)
{
pMarkArray[i] = FALSE;
}
// For all participants still in the roster,
// clear out the reserved flags and
// copy in new UserInfo
POSITION pos = m_MemberList.GetHeadPosition();
// lous: Preserve previous pos so we can check list integrity
POSITION prevpos = pos;
while (NULL != pos)
{
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
ASSERT(pMember);
pMember->RemovePf(PF_RESERVED);
UINT uNodeID = INVALID_GCCID;
if (PF_T120 & pMember->GetDwFlags())
{
uNodeID = pMember->GetGCCID();
}
for (UINT uNode = 0; uNode < uRosterNodes; uNode++)
{
if (uNodeID == pRoster->nodes[uNode].uNodeID)
{
nExistingParts++;
pMarkArray[uNode] = TRUE; // mark this node as "existing member"
ResetDataMember(pMember, pRoster->nodes[uNode].hUserData);
// lou: Check pos to make sure we didn't just wipe out the end of
// the list in ResetDataMember.
if (NULL == prevpos->pNext)
{
pos = NULL;
}
break;
}
}
// lou: Store previous pos so we can check list integrity.
prevpos = pos;
}
RemoveOldDataMembers(m_uDataMembers - nExistingParts);
if (pRoster->uNumNodes > nExistingParts)
{
#ifdef _DEBUG
UINT nAdded = 0;
#endif // _DEBUG
// At least one participant joined:
// find the new participant(s)
for (UINT uNode = 0; uNode < uRosterNodes; uNode++)
{
if (FALSE == pMarkArray[uNode]) // a new participant?
{
BOOL fLocal = FALSE;
CNmMember * pMember = NULL;
PVOID pvUserInfo;
UINT cbUserInfo;
ASSERT(g_pNodeController);
if (NOERROR != g_pNodeController->GetUserData(
pRoster->nodes[uNode].hUserData,
(GUID*) &g_csguidRostInfo,
&cbUserInfo,
&pvUserInfo))
{
pvUserInfo = NULL;
cbUserInfo = 0;
}
UINT uCaps;
UINT cbCaps;
PVOID pvCaps;
if (NOERROR != g_pNodeController->GetUserData(
pRoster->nodes[uNode].hUserData,
(GUID*) &g_csguidRosterCaps,
&cbCaps,
&pvCaps))
{
uCaps = 0;
}
else
{
ASSERT(pvCaps && (sizeof(uCaps) == cbCaps));
uCaps = *((PUINT)pvCaps);
}
PGUID pguidNodeId;
UINT cbNodeId;
if (NOERROR != g_pNodeController->GetUserData(
pRoster->nodes[uNode].hUserData,
(GUID*) &g_csguidNodeIdTag,
&cbNodeId,
(PVOID*) &pguidNodeId))
{
pguidNodeId = NULL;
}
else
{
if (sizeof(GUID) != cbNodeId)
{
pguidNodeId = NULL;
}
}
if (m_ourNodeID == pRoster->nodes[uNode].uNodeID)
{
fLocal = TRUE;
}
REFGUID rguidNodeId = pguidNodeId ? *pguidNodeId : GUID_NULL;
if (fLocal)
{
pMember = GetLocalMember();
}
else
{
pMember = MatchDataToH323Member(rguidNodeId, pRoster->nodes[uNode].uNodeID, pvUserInfo);
}
if(pMember)
{
AddDataToH323Member(pMember,
pvUserInfo,
cbUserInfo,
uCaps,
&pRoster->nodes[uNode]);
#ifdef _DEBUG
nAdded++; // a data participant was effectively added
#endif // _DEBUG
}
else
{
pMember = CreateDataMember(
fLocal,
rguidNodeId,
pvUserInfo,
cbUserInfo,
uCaps,
&pRoster->nodes[uNode]);
#ifdef _DEBUG
if (NULL != pMember)
{
nAdded++;
}
#endif // _DEBUG
AddMember(pMember, NULL);
}
}
}
// Validate that we did the right thing:
ASSERT(nAdded == (uRosterNodes - nExistingParts));
}
delete pMarkArray;
pMarkArray = NULL;
}
else
{
ERROR_OUT(("Couldn't allocate pMarkArray - no roster diff done"));
}
UINT uPrevDataMembers = m_uDataMembers;
m_uDataMembers = pRoster->uNumNodes;
// Check to decide if we should auto-terminate here..
if ((1 == pRoster->uNumNodes) &&
(uPrevDataMembers > 1) &&
(1 == m_uDataMembers))
{
if (!m_fServerMode)
{
LeaveConference(FALSE); // don't force (we could be inviting)
}
}
}
else
{
WARNING_OUT(("NULL pRoster passed to CConfObject::OnRosterChanged!"));
}
DebugExitBOOL(CConfObject::OnRosterChanged, bRet);
return bRet;
}
VOID CConfObject::AddMember(CNmMember * pMember, IH323Endpoint * pConnection)
{
DebugEntry(CConfObject::AddMember);
if (NULL == pMember)
{
ERROR_OUT(("AddMember - null member!"));
return;
}
NM_CONFERENCE_STATE oldNmState, newNmState;
GetState(&oldNmState);
m_MemberList.AddTail(pMember);
if(pConnection)
{
pMember->AddH323Endpoint(pConnection);
++m_uH323Endpoints;
CheckState(oldNmState);
GetState(&oldNmState);
}
m_uMembers++;
CheckState(oldNmState);
NotifySink((INmMember *) pMember, OnNotifyMemberAdded);
DebugExitVOID(CConfObject::AddMember);
}
VOID CConfObject::RemoveMember(POSITION pos)
{
DebugEntry(CConfObject::RemoveMember);
NM_CONFERENCE_STATE oldNmState, newNmState;
GetState(&oldNmState);
CNmMember * pMember = (CNmMember *) m_MemberList.RemoveAt(pos);
--m_uMembers;
if (pMember->FLocal())
{
// this is the local node:
m_pMemberLocal = NULL;
}
IH323Endpoint *pConnection = pMember->GetH323Endpoint();
if(pConnection)
{
pMember->DeleteH323Endpoint(pConnection);
--m_uH323Endpoints;
}
NotifySink((INmMember *) pMember, OnNotifyMemberRemoved);
pMember->Release();
CheckState(oldNmState);
DebugExitVOID(CConfObject::RemoveMember);
}
BOOL CConfObject::OnConferenceEnded()
{
DebugEntry(CConfObject::OnConferenceEnded);
BOOL bRet = TRUE;
switch (m_csState)
{
case CS_GOING_DOWN:
{
TRACE_OUT(("ConfEnded received (from CS_GOING_DOWN)"));
break;
}
case CS_RUNNING:
{
TRACE_OUT(("ConfEnded received (from CS_RUNNING)"));
break;
}
case CS_COMING_UP:
{
TRACE_OUT(("ConfEnded received (from CS_COMING_UP)"));
break;
}
default:
{
WARNING_OUT(("ConfEnded received (UNEXPECTED)"));
}
}
if (NULL != m_hConf)
{
m_hConf->ReleaseInterface();
m_hConf = NULL;
}
SetT120State(CS_TERMINATED);
TRACE_OUT(("OnConferenceEnded(), num participants is %d", m_uMembers));
// Empty the participant list:
NC_ROSTER FakeRoster;
ClearStruct(&FakeRoster);
FakeRoster.uConferenceID = m_uGCCConferenceID;
OnRosterChanged(&FakeRoster);
ASSERT(0 == m_ourNodeID);
ASSERT(0 == m_uDataMembers);
// Reset member variables that pertain to a conference
m_uGCCConferenceID = 0;
m_fServerMode = FALSE;
m_attendeePermissions = NM_PERMIT_ALL;
m_maxParticipants = (UINT)-1;
SysFreeString(m_bstrConfName);
m_bstrConfName = NULL;
SysFreeString(m_bstrConfPassword);
m_bstrConfPassword = NULL;
LeaveH323(TRUE /* fKeepAV */);
DebugExitBOOL(CConfObject::OnConferenceEnded, bRet);
return bRet;
}
BOOL CConfObject::OnConferenceStarted(CONF_HANDLE hConf, HRESULT hResult)
{
DebugEntry(CConfObject::OnConferenceStarted);
BOOL bRet = TRUE;
ASSERT(hConf == m_hConf);
switch (m_csState)
{
case CS_CREATING:
case CS_COMING_UP:
{
switch(hResult)
{
case S_OK:
TRACE_OUT(("ConfStarted received -> now running"));
SetT120State(CS_RUNNING);
break;
case UI_RC_INVALID_PASSWORD:
// nop, don't mess with state
// the conference is still coming up
// the incoming call handler will deal with this
break;
default:
SetT120State(CS_GOING_DOWN);
TRACE_OUT(("ConfStarted failed"));
break;
}
break;
}
default:
{
WARNING_OUT(("OnConferenceStarted received (UNEXPECTED)"));
break;
}
}
DebugExitBOOL(CConfObject::OnConferenceStarted, bRet);
return bRet;
}
VOID CConfObject::OnH323ChannelChange(DWORD dwFlags, BOOL fIncoming, BOOL fOpen, ICommChannel *pIChannel)
{
CConfObject *pco = ::GetConfObject();
IEnumNmMember *pEnumMember = NULL;
ULONG cFetched;
INmChannel *pINmChannel;
DWORD dwMediaFlag;
HRESULT hr;
// find the INmChannel instance that would be associated with the
// comm channel (pIChannel).
// note the current issues with making this work for any number/type of
// channels
//
// - CConfObject has 4 hardcoded instances of send/receive audio/video
// - Those instances aren't yet associated with the ICommChannel instance
// other than by media type and direction. For receive, new instances
// could even be created dynamically as rx channel requests are
// processed. For send, need to change CNmChannelAudio
// and CNmChannelVideo to keep a reference to ICommChannel before
// the channel open attempt occurs
// - There is no base interface common to CNmChannelAudio
// and CNmChannelVideo
// - There is no internal interface on CNmMember
//
CNmMember *pMember = NULL;
INmMember *pIMember = NULL;
if (PF_MEDIA_AUDIO & dwFlags)
{
dwMediaFlag = PF_MEDIA_AUDIO;
CNmChannelAudio *pChannelAudio;
if (fIncoming)
{
pChannelAudio = m_pChannelAudioRemote;
}
else
{
pChannelAudio = m_pChannelAudioLocal;
}
if (NULL != pChannelAudio)
{
pINmChannel = (INmChannel *) pChannelAudio;
if (fOpen)
{
pChannelAudio->CommChannelOpened(pIChannel);
}
else
{
pChannelAudio->CommChannelClosed();
}
// for every member associated with this channel, do the
// member update thing
hr = pChannelAudio->EnumMember(&pEnumMember);
if(pEnumMember)
{
ASSERT(hr == S_OK);
while(hr == S_OK)
{
pIMember = NULL;
hr = pEnumMember->Next(1, &pIMember, &cFetched);
if(!pIMember)
{
break;
}
else
{
ASSERT(hr == S_OK);
// this cast is ugly, but necessary because there is no
// real internal interface of CNmMember to query for.
// When in Rome........
pMember = (CNmMember *)pIMember;
if (fOpen)
{
pMember->AddPf(dwMediaFlag);
}
else
{
pMember->RemovePf(dwMediaFlag);
}
// ugly - OnMemberUpdated() should be a base interface
// method so that this code didn't have to be copied
// for the video case
pChannelAudio->OnMemberUpdated(pMember);
pco->OnMemberUpdated(pMember);
if (pMember->FLocal() && (NULL != m_hConf) && (CS_RUNNING == m_csState))
{
// m_hConf->UpdateUserData();
}
pMember->Release();
}
}
pEnumMember->Release();
}
NotifySink(pINmChannel, OnNotifyChannelUpdated);
}
}
else if (PF_MEDIA_VIDEO & dwFlags)
{
dwMediaFlag = PF_MEDIA_VIDEO;
CNmChannelVideo *pChannelVideo;
if (fIncoming)
{
pChannelVideo = m_pChannelVideoRemote;
}
else
{
pChannelVideo = m_pChannelVideoLocal;
}
if (NULL != pChannelVideo)
{
pINmChannel = (INmChannel *) pChannelVideo;
if (fOpen)
{
pChannelVideo->CommChannelOpened(pIChannel);
}
else
{
pChannelVideo->CommChannelClosed();
}
// for every member associated with this channel, do the
// member update thing
hr = pChannelVideo->EnumMember(&pEnumMember);
if(pEnumMember)
{
ASSERT(hr == S_OK);
while(hr == S_OK)
{
pIMember = NULL;
hr = pEnumMember->Next(1, &pIMember, &cFetched);
if(!pIMember)
{
break;
}
else
{
ASSERT(hr == S_OK);
// this cast is ugly, but necessary because there is no
// real internal interface of CNmMember to query for.
// When in Rome........
pMember = (CNmMember *)pIMember;
if (fOpen)
{
pMember->AddPf(dwMediaFlag);
}
else
{
pMember->RemovePf(dwMediaFlag);
}
// ugly - OnMemberUpdated() should be a base interface
// method so that this code didn't have to be copied
// from the audio case
pChannelVideo->OnMemberUpdated(pMember);
pco->OnMemberUpdated(pMember);
if (pMember->FLocal() && (NULL != m_hConf) && (CS_RUNNING == m_csState))
{
// m_hConf->UpdateUserData();
}
pMember->Release();
}
}
pEnumMember->Release();
}
NotifySink(pINmChannel, OnNotifyChannelUpdated);
}
}
else
ASSERT(0);
}
VOID CConfObject::OnAudioChannelStatus(ICommChannel *pIChannel, IH323Endpoint * lpConnection, DWORD dwStatus)
{
BOOL bIncoming = (pIChannel->IsSendChannel())? FALSE:TRUE;
CNmChannelAudio *pChannelAudio;
switch (dwStatus)
{
case CHANNEL_ACTIVE:
if (bIncoming)
{
pChannelAudio = m_pChannelAudioRemote;
}
else
{
pChannelAudio = m_pChannelAudioLocal;
}
if (NULL != pChannelAudio)
{
pChannelAudio->CommChannelActive(pIChannel);
}
break;
case CHANNEL_OPEN:
OnH323ChannelChange(PF_MEDIA_AUDIO, bIncoming, TRUE, pIChannel);
break;
case CHANNEL_CLOSED:
OnH323ChannelChange(PF_MEDIA_AUDIO, bIncoming, FALSE, pIChannel);
break;
default:
return;
}
}
VOID CConfObject::OnVideoChannelStatus(ICommChannel *pIChannel, IH323Endpoint * lpConnection, DWORD dwStatus)
{
BOOL bIncoming = (pIChannel->IsSendChannel())? FALSE:TRUE;
CNmChannelVideo *pChannelVideo;
switch (dwStatus)
{
case CHANNEL_ACTIVE:
if (bIncoming)
{
pChannelVideo = m_pChannelVideoRemote;
}
else
{
pChannelVideo = m_pChannelVideoLocal;
}
if (NULL != pChannelVideo)
{
pChannelVideo->CommChannelActive(pIChannel);
}
break;
case CHANNEL_OPEN:
OnH323ChannelChange(PF_MEDIA_VIDEO, bIncoming, TRUE, pIChannel);
break;
case CHANNEL_CLOSED:
OnH323ChannelChange(PF_MEDIA_VIDEO, bIncoming, FALSE, pIChannel);
break;
case CHANNEL_REJECTED:
case CHANNEL_OPEN_ERROR:
case CHANNEL_NO_CAPABILITY:
if(bIncoming)
{
if (NULL != m_pChannelVideoRemote)
{
m_pChannelVideoRemote->CommChannelError(dwStatus);
}
}
else
{
if (NULL != m_pChannelVideoLocal)
{
m_pChannelVideoLocal->CommChannelError(dwStatus);
}
}
break;
case CHANNEL_REMOTE_PAUSE_ON:
case CHANNEL_REMOTE_PAUSE_OFF:
if(bIncoming)
{
if (NULL != m_pChannelVideoRemote)
{
BOOL fPause = CHANNEL_REMOTE_PAUSE_ON == dwStatus;
m_pChannelVideoRemote->CommChannelRemotePaused(fPause);
}
}
else
{
if (NULL != m_pChannelVideoLocal)
{
BOOL fPause = CHANNEL_REMOTE_PAUSE_ON == dwStatus;
m_pChannelVideoLocal->CommChannelRemotePaused(fPause);
}
}
break;
default:
break;
}
}
VOID CConfObject::CreateMember(IH323Endpoint * pConnection, REFGUID rguidNode, UINT uNodeID)
{
ASSERT(g_pH323UI);
WCHAR wszRemoteName[MAX_CALLER_NAME];
if (FAILED(pConnection->GetRemoteUserName(wszRemoteName, MAX_CALLER_NAME)))
{
ERROR_OUT(("GetRemoteUserName() failed!"));
return;
}
// Add the local member
CNmMember * pMemberLocal = GetLocalMember();
if (NULL != pMemberLocal)
{
AddH323ToDataMember(pMemberLocal, NULL);
}
else
{
// We aren't already in the list, so add ourselves here:
BSTR bstrName = NULL;
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
if (NULL != pOprahNCUI)
{
bstrName = pOprahNCUI->GetUserName();
}
pMemberLocal = new CNmMember(bstrName, H323_GCCID_LOCAL,
PF_H323 | PF_LOCAL_NODE | PF_VER_CURRENT, 0, g_guidLocalNodeId, NULL, 0);
if (NULL != pMemberLocal)
{
AddMember(pMemberLocal, NULL);
ASSERT(NULL == m_pMemberLocal);
m_pMemberLocal = pMemberLocal;
}
}
// Add the remote member
CNmMember * pMemberRemote = MatchH323ToDataMembers(rguidNode, pConnection);
if (NULL != pMemberRemote)
{
AddH323ToDataMember(pMemberRemote, pConnection);
}
else
{
// BUGBUG: A version number should be added here, if possible
pMemberRemote = new CNmMember( wszRemoteName,
uNodeID,
PF_H323,
0,
rguidNode,
NULL,
0);
if (NULL != pMemberRemote)
{
AddMember(pMemberRemote, pConnection);
}
}
if (NULL != m_hConf && (CS_RUNNING == m_csState))
{
// m_hConf->UpdateUserData();
}
}
VOID CConfObject::OnH323Connected(IH323Endpoint * pConnection, DWORD dwFlags, BOOL fAddMember, REFGUID rguidNode)
{
HRESULT hr;
UINT ui;
ASSERT(NULL != pConnection);
// alloc and initialize media guids.
CMediaList MediaList;
GUID MediaType;
BOOL fEnableMedia;
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
if (NULL != pOprahNCUI)
{
MediaType = MEDIA_TYPE_H323VIDEO;
fEnableMedia = pOprahNCUI->IsSendVideoAllowed() && (dwFlags & CRPCF_VIDEO);
MediaList.EnableMedia(&MediaType, TRUE /*send */, fEnableMedia);
fEnableMedia = pOprahNCUI->IsReceiveVideoAllowed() && (dwFlags & CRPCF_VIDEO);
MediaList.EnableMedia(&MediaType, FALSE /* recv, NOT send */, fEnableMedia);
MediaType = MEDIA_TYPE_H323AUDIO;
fEnableMedia = pOprahNCUI->IsAudioAllowed() && (dwFlags & CRPCF_AUDIO);
MediaList.EnableMedia(&MediaType, TRUE /* send */, fEnableMedia);
MediaList.EnableMedia(&MediaType, FALSE /* recv, NOT send */, fEnableMedia);
MediaType = MEDIA_TYPE_H323_T120;
fEnableMedia = (dwFlags & CRPCF_DATA);
MediaList.EnableMedia(&MediaType, TRUE /* send */, fEnableMedia);
MediaList.EnableMedia(&MediaType, FALSE /* recv, NOT send */, fEnableMedia);
}
hr = MediaList.ResolveSendFormats(pConnection);
if(!(SUCCEEDED(hr)))
{
// Well, there is no way we can ever open any send channel. But It is a
// product requirement to keep the connection up just in case the other
// endpoint(s) ever wants to open a send video channel to this endpoint.
}
ICommChannel* pChannelT120 = CreateT120Channel(pConnection, &MediaList);
CreateAVChannels(pConnection, &MediaList);
if (pChannelT120)
{
OpenT120Channel(pConnection, &MediaList, pChannelT120);
// no need to hold onto the T120 channel
pChannelT120->Release();
}
OpenAVChannels(pConnection, &MediaList);
if (fAddMember)
{
CreateMember(pConnection, rguidNode, H323_GCCID_REMOTE);
if (dwFlags & (CRPCF_AUDIO | CRPCF_VIDEO))
{
CNmMember* pMemberLocal = GetLocalMember();
if (pMemberLocal)
{
AddMemberToAVChannels(pMemberLocal);
}
CNmMember* pMemberRemote = PMemberFromH323Endpoint(pConnection);
if (pMemberRemote)
{
AddMemberToAVChannels(pMemberRemote);
}
}
}
}
VOID CConfObject::OnH323Disconnected(IH323Endpoint * pConnection, BOOL fHasAV)
{
DebugEntry(CConfObject::OnH323Disconnected);
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
POSITION oldpos = pos;
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
ASSERT(pMember);
if (pMember->GetH323Endpoint() == pConnection)
{
if (fHasAV)
{
RemoveMemberFromAVChannels(pMember);
}
if (0 == (PF_T120 & pMember->GetDwFlags()))
{
// This is an H323 only participant, so remove now:
RemoveMember(oldpos);
}
else
{
RemoveH323FromDataMember(pMember, pConnection);
}
}
}
CNmMember *pLocalMember = GetLocalMember();
if (pLocalMember)
{
if (fHasAV)
{
RemoveMemberFromAVChannels(pLocalMember);
}
if (0 == m_uH323Endpoints)
{
if (0 == (PF_T120 & pLocalMember->GetDwFlags()))
{
// This is an H323 only participant, so remove now:
RemoveMember(pLocalMember);
}
else
{
RemoveH323FromDataMember(pLocalMember, NULL);
}
}
}
if (fHasAV)
{
DestroyAVChannels();
}
#ifdef REPORT_ALL_ERRORS
DWORD dwSummary;
dwSummary = pConnection->GetSummaryCode()
if(CCR_REMOTE_MEDIA_ERROR == dwSummary)
{
::PostConfMsgBox(IDS_REMOTE_MEDIA_ERROR);
}
#endif
if ((NULL != m_hConf) && (CS_RUNNING == m_csState))
{
// m_hConf->UpdateUserData();
}
DebugExitVOID(CConfObject::OnH323Disconnected);
}
VOID CConfObject::OnT120Connected(IH323Endpoint * pConnection, UINT uNodeID)
{
CNmMember *pMember = PMemberFromH323Endpoint(pConnection);
if (pMember)
{
// save away the GCC id so that we can match this up when the member is added
pMember->SetGCCID(uNodeID);
}
else
{
CreateMember(pConnection, GUID_NULL, uNodeID);
}
}
// StoreAndVerifyMemberUserData
//
// Processes a member's user data and stores them for the GetUserData API call.
// If security data is among the user data, verification against the transport-level
// credentials is performed.
// Returns FALSE if security verification fails, TRUE otherwise.
BOOL StoreAndVerifyMemberUserData(CNmMember * pMember, ROSTER_DATA_HANDLE hData)
{
BOOL rc = TRUE;
BOOL fUserDataSet;
GCCNodeRecord * pRosterEntry = (GCCNodeRecord *)hData;
GCCUserData ** ppUserData = pRosterEntry->user_data_list;
for (int i = 0; i < pRosterEntry->number_of_user_data_members; i++)
{
fUserDataSet = FALSE;
/* Always False if ((int)ppUserData[i]->octet_string->length - sizeof(GUID) < 0)
{
WARNING_OUT(("StoreAndVerifyMemberUserData: bad user data"));
rc = FALSE;
break;
}*/
if (!pMember->FLocal() && 0 == CompareGuid((GUID *)ppUserData[i]->octet_string->value,(GUID *)&g_csguidSecurity))
{
PBYTE pb = NULL;
ULONG cb = 0;
if (pMember->GetSecurityData(&pb,&cb))
{
//
// Check to make sure that the current user data matches
// the transport security data.
//
if (memcmp(pb,ppUserData[i]->octet_string->value + sizeof(GUID),
ppUserData[i]->octet_string->length - sizeof(GUID) - 1))
{
//
// This should NOT happen. Either there is a bug
// in the security code (credentials failed up update
// in the transport or the like), or someone is trying
// to deceive us.
ERROR_OUT(("SECURITYDATA MISMATCH"));
fUserDataSet = TRUE; // so we don't do it below.
rc = FALSE;
}
}
else {
WARNING_OUT(("StoreAndVerifyMemberUserData: failed to get security data"));
rc = FALSE;
}
CoTaskMemFree(pb);
}
if ( FALSE == fUserDataSet )
{
pMember->SetUserData(*(GUID *)ppUserData[i]->octet_string->value,
(BYTE *)ppUserData[i]->octet_string->value + sizeof(GUID),
ppUserData[i]->octet_string->length - sizeof(GUID));
}
}
return rc;
}
VOID CConfObject::ResetDataMember( CNmMember * pMember,
ROSTER_DATA_HANDLE hData)
{
DebugEntry(CConfObject::ResetDataMember);
pMember->AddPf(PF_RESERVED);
pMember->SetUserInfo(NULL, 0);
UINT cbData;
PVOID pData;
ASSERT(g_pNodeController);
if (NOERROR == g_pNodeController->GetUserData(
hData,
(GUID*) &g_csguidRostInfo,
&cbData,
&pData))
{
pMember->SetUserInfo(pData, cbData);
}
UINT cbCaps;
PVOID pvCaps;
if (NOERROR != g_pNodeController->GetUserData(
hData,
(GUID*) &g_csguidRosterCaps,
&cbCaps,
&pvCaps))
{
WARNING_OUT(("roster update is missing caps information"));
}
else
{
ASSERT(NULL != pvCaps);
ASSERT(sizeof(ULONG) == cbCaps);
pMember->SetCaps( *((PULONG)pvCaps) );
}
if (StoreAndVerifyMemberUserData(pMember, hData) == FALSE) {
// Need to disconnect the conference in this case.
WARNING_OUT(("ResetDataMember Security Warning: Authentication data could not be verified."));
}
NotifySink((INmMember *) pMember, OnNotifyMemberUpdated);
DebugExitVOID(CConfObject::ResetDataMember);
}
VOID CConfObject::RemoveOldDataMembers(int nExpected)
{
DebugEntry(CConfObject::RemoveOldDataMembers);
#ifdef _DEBUG
int nRemoved = 0;
#endif // _DEBUG
ASSERT(nExpected >= 0);
if (nExpected > 0)
{
// At least one participant left:
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
POSITION oldpos = pos;
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
ASSERT(pMember);
DWORD dwFlags = pMember->GetDwFlags();
if (!(PF_RESERVED & dwFlags))
{
// This one is not in the data call:
TRACE_OUT(("CConfObject Roster: %ls (%d) has left.",
pMember->GetName(), pMember->GetGCCID()));
#ifdef _DEBUG
if (dwFlags & PF_T120)
{
nRemoved++;
}
#endif // _DEBUG
if (0 == (dwFlags & PF_H323))
{
// If they were data only, then remove:
RemoveMember(oldpos);
}
else
{
pMember->RemovePf(PF_DATA_ALL);
pMember->SetGCCID(pMember->FLocal() ? H323_GCCID_LOCAL : H323_GCCID_REMOTE);
pMember->SetGccIdParent(INVALID_GCCID);
pMember->SetCaps(0);
pMember->SetUserInfo(NULL, 0);
NotifySink((INmMember *) pMember, OnNotifyMemberUpdated);
}
}
}
// Validate that we did the right thing:
ASSERT(nRemoved == nExpected);
}
DebugExitVOID(CConfObject::RemoveOldDataMembers);
}
CNmMember *CConfObject::MatchDataToH323Member( REFGUID rguidNode,
UINT uNodeId,
PVOID pvUserInfo)
{
DebugEntry(CConfObject::MatchDataToH323Member);
CNmMember *pMemberRet = NULL;
BOOL bRet = FALSE;
if (GUID_NULL != rguidNode)
{
// try matching up guids
pMemberRet = PMemberFromNodeGuid(rguidNode);
}
if (NULL == pMemberRet)
{
// try matching up node ids
pMemberRet = PMemberFromGCCID(uNodeId);
}
if ((NULL == pMemberRet) && pvUserInfo)
{
// All else failed try mathcing IP addresses
CRosterInfo ri;
if(SUCCEEDED(ri.Load(pvUserInfo)))
{
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
SOCKADDR_IN sin;
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
IH323Endpoint * pConnection = pMember->GetH323Endpoint();
if (pConnection && (S_OK == pConnection->GetRemoteUserAddr(&sin)))
{
TCHAR szAudioIP[MAX_PATH];
TCHAR szDataIP[MAX_PATH];
HROSTINFO hRI = NULL;
// BUGBUG: UNICODE issues?
lstrcpyn(szAudioIP, inet_ntoa(sin.sin_addr), CCHMAX(szAudioIP));
while (SUCCEEDED(ri.ExtractItem(&hRI,
g_cwszIPTag,
szDataIP,
CCHMAX(szDataIP))))
{
TRACE_OUT(("Comparing data IP \"%s\" with "
"audio IP \"%s\"", szDataIP, szAudioIP));
if (0 == lstrcmp(szDataIP, szAudioIP))
{
pMemberRet = pMember;
break; // out of outer while loop
}
}
}
}
}
}
DebugExitPVOID(CConfObject::MatchDataToH323Member, pMemberRet);
return pMemberRet;
}
VOID CConfObject::AddDataToH323Member( CNmMember * pMember,
PVOID pvUserInfo,
UINT cbUserInfo,
UINT uCaps,
NC_ROSTER_NODE_ENTRY* pRosterNode)
{
DebugEntry(CConfObject::AddDataToH323Member);
ASSERT(pMember);
ASSERT(NULL != pRosterNode);
DWORD dwFlags = pMember->GetDwFlags();
ASSERT(0 == ((PF_MEDIA_DATA | PF_T120) & dwFlags));
dwFlags |= (PF_T120 | PF_MEDIA_DATA | PF_CA_DETACHED);
// Add version information
dwFlags = (dwFlags & ~PF_VER_MASK) |
PF_VER_FromUserData(pRosterNode->hUserData);
pMember->SetDwFlags(dwFlags);
pMember->SetGCCID(pRosterNode->uNodeID);
pMember->SetGccIdParent(pRosterNode->uSuperiorNodeID);
ASSERT(0 == pMember->GetCaps());
pMember->SetCaps(uCaps);
pMember->SetUserInfo(pvUserInfo, cbUserInfo);
NotifySink((INmMember *) pMember, OnNotifyMemberUpdated);
ROSTER_DATA_HANDLE hData = pRosterNode->hUserData;
if (StoreAndVerifyMemberUserData(pMember, hData) == FALSE) {
// Need to disconnect the conference in this case.
WARNING_OUT(("AddDataToH323Member Security Warning: Authentication data could not be verified."));
}
DebugExitVOID(CConfObject::AddDataToH323Member);
}
CNmMember * CConfObject::CreateDataMember(BOOL fLocal,
REFGUID rguidNode,
PVOID pvUserInfo,
UINT cbUserInfo,
UINT uCaps,
NC_ROSTER_NODE_ENTRY* pRosterNode)
{
DebugEntry(CConfObject::CreateDataMember);
ASSERT(NULL != pRosterNode);
DWORD dwFlags = PF_T120 | PF_MEDIA_DATA | PF_CA_DETACHED;
if (fLocal)
{
dwFlags |= (PF_LOCAL_NODE | PF_VER_CURRENT);
}
if (pRosterNode->fMCU)
{
dwFlags |= PF_T120_MCU;
}
if (0 != cbUserInfo)
{
dwFlags = (dwFlags & ~PF_VER_MASK)
| PF_VER_FromUserData(pRosterNode->hUserData);
}
CNmMember * pMember = new CNmMember(pRosterNode->pwszNodeName,
pRosterNode->uNodeID,
dwFlags,
uCaps,
rguidNode,
pvUserInfo,
cbUserInfo);
if (NULL != pMember)
{
pMember->SetGccIdParent(pRosterNode->uSuperiorNodeID);
if (fLocal)
{
ASSERT(NULL == m_pMemberLocal);
m_pMemberLocal = pMember;
}
}
ROSTER_DATA_HANDLE hData = pRosterNode->hUserData;
if (StoreAndVerifyMemberUserData(pMember, hData) == FALSE) {
// Need to disconnect the conference in this case.
WARNING_OUT(("CreateDataMember Security Warning: Authentication data could not be verified."));
}
TRACE_OUT(("CConfObject Roster: %ls (%d) has joined.", pRosterNode->pwszNodeName, pRosterNode->uNodeID));
DebugExitPVOID(CConfObject::CreateDataMember, pMember);
return pMember;
}
CNmMember * CConfObject::MatchH323ToDataMembers(REFGUID rguidNodeId,
IH323Endpoint * pConnection)
{
DebugEntry(CConfObject::MatchH323ToDataMembers);
CNmMember * pMemberRet = NULL;
// This is currently called only by OnH323Connected(). Terminal label isn't assigned yet.
// so there is no need yet to INSERT SEARCH FOR MATCHING TERMINAL LABEL HERE
if (GUID_NULL != rguidNodeId)
{
pMemberRet = PMemberFromNodeGuid(rguidNodeId);
}
else
{
SOCKADDR_IN sin;
if (S_OK == pConnection->GetRemoteUserAddr(&sin))
{
TCHAR szAudioIP[MAX_PATH];
lstrcpyn(szAudioIP, inet_ntoa(sin.sin_addr), CCHMAX(szAudioIP));
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
// need to try to match IP addresses
// this is how things were done in NM2.11 and earlier
TCHAR szDataIP[MAX_PATH];
HROSTINFO hRI = NULL;
CRosterInfo ri;
if (SUCCEEDED(ri.Load(pMember->GetUserInfo())) )
{
while (SUCCEEDED(ri.ExtractItem(&hRI,
g_cwszIPTag,
szDataIP,
CCHMAX(szDataIP))))
{
TRACE_OUT(("Comparing data IP \"%s\" with "
"h323 IP \"%s\"", szDataIP, szAudioIP));
if (0 == lstrcmp(szDataIP, szAudioIP))
{
// close enough
return pMember;
}
}
}
}
}
}
DebugExitPVOID(CConfObject::MatchH323ToDataMembers, pMemberRet);
return pMemberRet;
}
VOID CConfObject::AddMemberToAVChannels(CNmMember *pMember)
{
CNmChannelAudio *pChannelAudio;
CNmChannelVideo *pChannelVideo;
if (pMember->FLocal())
{
pChannelAudio = m_pChannelAudioLocal;
pChannelVideo = m_pChannelVideoLocal;
}
else
{
pChannelAudio = m_pChannelAudioRemote;
pChannelVideo = m_pChannelVideoRemote;
}
if (pChannelAudio)
{
pChannelAudio->OnMemberAdded(pMember);
// set media flags if channel is open
if(S_OK == pChannelAudio->IsActive())
{
pMember->AddPf(PF_MEDIA_AUDIO);
pChannelAudio->OnMemberUpdated(pMember);
OnMemberUpdated(pMember);
}
}
if (pChannelVideo)
{
pChannelVideo->OnMemberAdded(pMember);
// set media flags if channel is open
if(S_OK == pChannelVideo->IsActive())
{
pMember->AddPf(PF_MEDIA_VIDEO);
pChannelVideo->OnMemberUpdated(pMember);
OnMemberUpdated(pMember);
}
}
}
VOID CConfObject::RemoveMemberFromAVChannels(CNmMember *pMember)
{
CNmChannelAudio *pChannelAudio;
CNmChannelVideo *pChannelVideo;
if (pMember->FLocal())
{
pChannelAudio = m_pChannelAudioLocal;
pChannelVideo = m_pChannelVideoLocal;
}
else
{
pChannelAudio = m_pChannelAudioRemote;
pChannelVideo = m_pChannelVideoRemote;
}
if ((NULL != pChannelVideo) && (PF_MEDIA_VIDEO & pMember->GetDwFlags()))
{
pMember->RemovePf(PF_MEDIA_VIDEO);
pChannelVideo->OnMemberRemoved(pMember);
OnMemberUpdated(pMember);
}
if ((NULL != pChannelAudio) && (PF_MEDIA_AUDIO & pMember->GetDwFlags()))
{
pMember->RemovePf(PF_MEDIA_AUDIO);
pChannelAudio->OnMemberRemoved(pMember);
OnMemberUpdated(pMember);
}
}
/* C R E A T E A V C H A N N E L S */
/*-------------------------------------------------------------------------
%%Function: CreateAVChannels
Create AV channels.
-------------------------------------------------------------------------*/
VOID CConfObject::CreateAVChannels(IH323Endpoint * pConnection, CMediaList* pMediaList)
{
HRESULT hr;
GUID MediaGuid;
ICommChannel *pCommChannel = NULL;
MediaGuid = MEDIA_TYPE_H323AUDIO;
if (pMediaList->IsInSendList(&MediaGuid))
{
m_pChannelAudioLocal = new CNmChannelAudio(FALSE /* fIncoming */);
if (NULL != m_pChannelAudioLocal)
{
hr = pConnection->CreateCommChannel(&MediaGuid, &pCommChannel, TRUE /* fSend*/);
ASSERT(SUCCEEDED(hr) && (NULL != pCommChannel));
//if(SUCCEEDED(hr) && (NULL != pCommChannel))
{
NotifySink((INmChannel *) m_pChannelAudioLocal, OnNotifyChannelAdded);
m_pChannelAudioLocal->OnConnected(pConnection, pCommChannel);
hr = pCommChannel->EnableOpen(TRUE);
ASSERT(SUCCEEDED(hr));
}
pCommChannel->Release();
pCommChannel = NULL; // bug detection that can be removed later
}
}
if (pMediaList->IsInRecvList(&MediaGuid))
{
m_pChannelAudioRemote = new CNmChannelAudio(TRUE /* fIncoming */);
if (NULL != m_pChannelAudioRemote)
{
hr = pConnection->CreateCommChannel(&MediaGuid, &pCommChannel, FALSE /* fSend*/);
ASSERT(SUCCEEDED(hr) && (NULL != pCommChannel));
//if(SUCCEEDED(hr) && (NULL != pCommChannel))
{
NotifySink((INmChannel *) m_pChannelAudioRemote, OnNotifyChannelAdded);
m_pChannelAudioRemote->OnConnected(pConnection, pCommChannel);
hr = pCommChannel->EnableOpen(TRUE);
ASSERT(SUCCEEDED(hr));
}
pCommChannel->Release();
pCommChannel = NULL; // bug detection that can be removed later
}
}
MediaGuid = MEDIA_TYPE_H323VIDEO; // now doing video channels
if (pMediaList->IsInSendList(&MediaGuid))
{
m_pChannelVideoLocal = CNmChannelVideo::CreateChannel(FALSE /* fIncoming */);
if (NULL != m_pChannelVideoLocal)
{
BOOL fCreated = FALSE;
// check for previous existence of preview stream/preview channel
if(NULL == (pCommChannel= m_pChannelVideoLocal->GetPreviewCommChannel()))
{
hr = pConnection->CreateCommChannel(&MediaGuid, &pCommChannel, TRUE /* fSend*/);
ASSERT(SUCCEEDED(hr) && (NULL != pCommChannel));
fCreated = TRUE;
}
else
{
pCommChannel->SetAdviseInterface(m_pIH323ConfAdvise);
}
//if(SUCCEEDED(hr) && (NULL != pCommChannel))
{
NotifySink((INmChannel *) m_pChannelVideoLocal, OnNotifyChannelAdded);
m_pChannelVideoLocal->OnConnected(pConnection, pCommChannel);
hr = pCommChannel->EnableOpen(TRUE);
ASSERT(SUCCEEDED(hr));
}
if (fCreated)
pCommChannel->Release();
pCommChannel = NULL; // bug detection that can be removed later
}
}
if (pMediaList->IsInRecvList(&MediaGuid))
{
m_pChannelVideoRemote = CNmChannelVideo::CreateChannel(TRUE /* fIncoming */);
if (NULL != m_pChannelVideoRemote)
{
BOOL fCreated = FALSE;
// check for previous existence of preview stream/preview channel
if(NULL == (pCommChannel= m_pChannelVideoRemote->GetCommChannel()))
{
hr = pConnection->CreateCommChannel(&MediaGuid, &pCommChannel, FALSE /* fSend*/);
fCreated = TRUE;
}
else
{
pCommChannel->SetAdviseInterface(m_pIH323ConfAdvise);
}
ASSERT(SUCCEEDED(hr) && (NULL != pCommChannel));
//if(SUCCEEDED(hr) && (NULL != pCommChannel))
{
NotifySink((INmChannel *) m_pChannelVideoRemote, OnNotifyChannelAdded);
m_pChannelVideoRemote->OnConnected(pConnection, pCommChannel);
hr = pCommChannel->EnableOpen(TRUE);
ASSERT(SUCCEEDED(hr));
}
if (fCreated)
pCommChannel->Release();
}
}
}
/* O P E N A V C H A N N E L S */
/*-------------------------------------------------------------------------
%%Function: OpenAVChannels
Open AV channels.
-------------------------------------------------------------------------*/
VOID CConfObject::OpenAVChannels(IH323Endpoint * pConnection, CMediaList* pMediaList)
{
MEDIA_FORMAT_ID idLocal;
if(m_pChannelAudioLocal)
{
if (pMediaList->GetSendFormatLocalID(MEDIA_TYPE_H323AUDIO, &idLocal))
{
m_pChannelAudioLocal->SetFormat(idLocal);
// open only if a valid negotiated format exists.
// it won't hurt to always call the Open() method, but there is
// no need to. This will and should probably change. Calling
// this with INVALID_MEDIA_FORMAT results in a call to the event
// handler for the channel, notifying the upper layer(s) that the
// channel could not be opened due to no compatible caps. User
// feedback could be much improved if it took advantage of this.
if(idLocal != INVALID_MEDIA_FORMAT)
{
m_pChannelAudioLocal->Open();
}
}
}
if(m_pChannelVideoLocal)
{
if (pMediaList->GetSendFormatLocalID(MEDIA_TYPE_H323VIDEO, &idLocal))
{
m_pChannelVideoLocal->SetFormat(idLocal);
if(m_pChannelVideoLocal->IsPreviewEnabled())
{
// open only if a valid negotiated format exists. see comments
// in the MEDIA_TYPE_H323AUDIO case above
if(idLocal != INVALID_MEDIA_FORMAT)
{
m_pChannelVideoLocal->Open();
}
}
}
}
}
ICommChannel* CConfObject::CreateT120Channel(IH323Endpoint * pConnection, CMediaList* pMediaList)
{
ICommChannel *pChannelT120 = NULL;
// create a T.120 channel stub
GUID MediaGuid = MEDIA_TYPE_H323_T120;
if (pMediaList->IsInSendList(&MediaGuid))
{
HRESULT hr = pConnection->CreateCommChannel(&MediaGuid, &pChannelT120, TRUE /* fSend*/);
if(SUCCEEDED(hr))
{
ASSERT(NULL != pChannelT120);
hr = pChannelT120->EnableOpen(TRUE);
ASSERT(SUCCEEDED(hr));
}
}
return pChannelT120;
}
VOID CConfObject::OpenT120Channel(IH323Endpoint * pConnection, CMediaList* pMediaList, ICommChannel* pChannelT120)
{
if(pChannelT120)
{
MEDIA_FORMAT_ID idLocal;
if (pMediaList->GetSendFormatLocalID(MEDIA_TYPE_H323_T120, &idLocal))
{
// T.120 channels are different. Always call the Open() method
// If there are no common T.120 capabilities. This lets the
// channel event handler know of the absence of remote T.120
// caps.
// The T.120 call side of things is tied into the T.120 channel
// event handler.
pChannelT120->Open(idLocal, pConnection);
}
}
}
/* D E S T R O Y A V C H A N N E L S */
/*-------------------------------------------------------------------------
%%Function: DestroyAVChannels
Destroy AV channels.
-------------------------------------------------------------------------*/
VOID CConfObject::DestroyAVChannels()
{
if (NULL != m_pChannelAudioLocal)
{
NotifySink((INmChannel *) m_pChannelAudioLocal, OnNotifyChannelRemoved);
m_pChannelAudioLocal->Release();
m_pChannelAudioLocal = NULL;
}
if (NULL != m_pChannelAudioRemote)
{
NotifySink((INmChannel *) m_pChannelAudioRemote, OnNotifyChannelRemoved);
m_pChannelAudioRemote->Release();
m_pChannelAudioRemote = NULL;
}
if (NULL != m_pChannelVideoLocal)
{
m_pChannelVideoLocal->OnDisconnected();
NotifySink((INmChannel *) m_pChannelVideoLocal, OnNotifyChannelRemoved);
m_pChannelVideoLocal->Release();
m_pChannelVideoLocal = NULL;
}
if (NULL != m_pChannelVideoRemote)
{
m_pChannelVideoRemote->OnDisconnected();
NotifySink((INmChannel *) m_pChannelVideoRemote, OnNotifyChannelRemoved);
m_pChannelVideoRemote->Release();
m_pChannelVideoRemote = NULL;
}
}
HRESULT CConfObject::GetMediaChannel (GUID *pmediaID,BOOL bSendDirection, IMediaChannel **ppI)
{
*ppI = NULL;
if (*pmediaID == MEDIA_TYPE_H323AUDIO)
{
CNmChannelAudio *pAudChan = (bSendDirection ? m_pChannelAudioLocal : m_pChannelAudioRemote);
*ppI = (pAudChan ? pAudChan->GetMediaChannelInterface() : NULL);
}
else if (*pmediaID == MEDIA_TYPE_H323VIDEO)
{
CNmChannelVideo *pVidChan = (bSendDirection ? m_pChannelVideoLocal : m_pChannelVideoRemote);
*ppI = (pVidChan ? pVidChan->GetMediaChannelInterface() : NULL);
}
return (*ppI == NULL ? E_NOINTERFACE : S_OK);
}
VOID CConfObject::AddH323ToDataMember(CNmMember * pMember, IH323Endpoint * pConnection)
{
DebugEntry(CConfObject::AddH323ToDataMember);
// Add the H323 flag bit to the member:
pMember->AddPf(PF_H323);
if (pConnection)
{
ASSERT(NULL == pMember->GetH323Endpoint());
pMember->AddH323Endpoint(pConnection);
++m_uH323Endpoints;
}
DebugExitVOID(CConfObject::AddH323ToDataMember);
}
VOID CConfObject::RemoveH323FromDataMember(CNmMember * pMember, IH323Endpoint * pConnection)
{
DebugEntry(CConfObject::RemoveH323FromDataMember);
// Remove the H323 flag from the member:
pMember->RemovePf(PF_H323);
if (pConnection)
{
pMember->DeleteH323Endpoint(pConnection);
--m_uH323Endpoints;
}
DebugExitVOID(CConfObject::RemoveH323FromDataMember);
}
VOID CConfObject::OnMemberUpdated(INmMember *pMember)
{
NotifySink(pMember, OnNotifyMemberUpdated);
}
VOID CConfObject::OnChannelUpdated(INmChannel *pChannel)
{
NotifySink(pChannel, OnNotifyChannelUpdated);
}
VOID CConfObject::SetT120State(CONFSTATE state)
{
NM_CONFERENCE_STATE oldNmState;
GetState(&oldNmState);
m_csState = state;
if ( state == CS_TERMINATED )
m_fSecure = FALSE; // Reset secure flag
CheckState(oldNmState);
}
VOID CConfObject::CheckState(NM_CONFERENCE_STATE oldNmState)
{
NM_CONFERENCE_STATE newNmState;
GetState(&newNmState);
if (oldNmState != newNmState)
{
NotifySink((PVOID) newNmState, OnNotifyStateChanged);
if (NM_CONFERENCE_IDLE == newNmState)
{
_EraseDataChannelGUIDS();
m_fConferenceCreated = FALSE;
}
}
}
void CConfObject::RemoveDataChannelGUID(REFGUID rguid)
{
POSITION pCur = m_DataChannelGUIDS.GetHeadPosition();
POSITION pNext = pCur;
while(pCur)
{
GUID* pG = reinterpret_cast<GUID*>(m_DataChannelGUIDS.GetNext(pNext));
if(*pG == rguid)
{
m_DataChannelGUIDS.RemoveAt(pCur);
}
pCur = pNext;
}
}
void CConfObject::_EraseDataChannelGUIDS(void)
{
POSITION pCur = m_DataChannelGUIDS.GetHeadPosition();
while(pCur)
{
GUID* pG = reinterpret_cast<GUID*>(m_DataChannelGUIDS.GetNext(pCur));
delete pG;
}
m_DataChannelGUIDS.EmptyList();
}
ULONG CConfObject::AddRef(void)
{
return ++m_cRef;
}
ULONG CConfObject::Release(void)
{
ASSERT(m_cRef > 0);
if (m_cRef > 0)
{
m_cRef--;
}
ULONG cRef = m_cRef;
if (0 == cRef)
{
delete this;
}
return cRef;
}
HRESULT STDMETHODCALLTYPE CConfObject::QueryInterface(REFIID riid, PVOID *ppv)
{
HRESULT hr = S_OK;
if((riid == IID_INmConference2) || (riid == IID_INmConference) || (riid == IID_IUnknown))
{
*ppv = (INmConference2 *)this;
ApiDebugMsg(("CConfObject::QueryInterface()"));
}
else if (riid == IID_IConnectionPointContainer)
{
*ppv = (IConnectionPointContainer *) this;
ApiDebugMsg(("CConfObject::QueryInterface(): Returning IConnectionPointContainer."));
}
else
{
hr = E_NOINTERFACE;
*ppv = NULL;
ApiDebugMsg(("CConfObject::QueryInterface(): Called on unknown interface."));
}
if (S_OK == hr)
{
AddRef();
}
return hr;
}
HRESULT CConfObject::GetName(BSTR *pbstrName)
{
HRESULT hr = E_POINTER;
if (NULL != pbstrName)
{
*pbstrName = SysAllocString(m_bstrConfName);
hr = *pbstrName ? S_OK : E_FAIL;
}
return hr;
}
HRESULT CConfObject::GetID(ULONG *puID)
{
HRESULT hr = E_POINTER;
if (NULL != puID)
{
*puID = m_uGCCConferenceID;
hr = S_OK;
}
return hr;
}
HRESULT CConfObject::GetState(NM_CONFERENCE_STATE *pState)
{
HRESULT hr = E_POINTER;
if (NULL != pState)
{
hr = S_OK;
switch (m_csState)
{
// Note: All states are valid (at least, for now)
case CS_CREATING:
case CS_UNINITIALIZED:
case CS_TERMINATED:
if (0 == m_uH323Endpoints)
{
*pState = NM_CONFERENCE_IDLE;
break;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// else fall through !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//////////////////////////////////////////////////////////////////////////
case CS_COMING_UP:
case CS_GOING_DOWN:
case CS_RUNNING:
if (m_uMembers < 2)
{
if (m_fServerMode)
{
*pState = NM_CONFERENCE_WAITING;
}
else
{
*pState = NM_CONFERENCE_INITIALIZING;
}
}
else
{
*pState = NM_CONFERENCE_ACTIVE;
}
break;
default:
hr = E_FAIL;
break;
}
}
return hr;
}
HRESULT CConfObject::GetNmchCaps(ULONG *puchCaps)
{
HRESULT hr = E_POINTER;
// BUGBUG: this returns secure cap only, used to be NOTIMPL
if (NULL != puchCaps)
{
*puchCaps = m_fSecure ? NMCH_SECURE : 0;
hr = S_OK;
}
return hr;
}
HRESULT CConfObject::GetTopProvider(INmMember **ppMember)
{
CNmMember *pMemberRet = NULL;
HRESULT hr = E_POINTER;
if (NULL != ppMember)
{
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
CNmMember *pMember = (CNmMember *) m_MemberList.GetNext(pos);
ASSERT(pMember);
if (pMember->FTopProvider())
{
// We have found the top provider
pMemberRet = pMember;
break;
}
}
*ppMember = pMemberRet;
hr = (NULL != pMemberRet) ? S_OK : S_FALSE;
}
return hr;
}
HRESULT CConfObject::EnumMember(IEnumNmMember **ppEnum)
{
HRESULT hr = E_POINTER;
if (NULL != ppEnum)
{
*ppEnum = new CEnumNmMember(&m_MemberList, m_uMembers);
hr = (NULL != *ppEnum) ? S_OK : E_OUTOFMEMORY;
}
return hr;
}
HRESULT CConfObject::GetMemberCount(ULONG *puCount)
{
HRESULT hr = E_POINTER;
if (NULL != puCount)
{
*puCount = m_uMembers;
hr = S_OK;
}
return hr;
}
HRESULT CConfObject::EnumChannel(IEnumNmChannel **ppEnum)
{
HRESULT hr = E_POINTER;
if (NULL != ppEnum)
{
COBLIST ChannelList;
ULONG cChannels = 0;
if (NULL != m_pChannelAudioLocal)
{
ChannelList.AddTail(m_pChannelAudioLocal);
++cChannels;
}
if (NULL != m_pChannelAudioRemote)
{
ChannelList.AddTail(m_pChannelAudioRemote);
++cChannels;
}
if (NULL != m_pChannelVideoLocal)
{
ChannelList.AddTail(m_pChannelVideoLocal);
++cChannels;
}
if (NULL != m_pChannelVideoRemote)
{
ChannelList.AddTail(m_pChannelVideoRemote);
++cChannels;
}
*ppEnum = new CEnumNmChannel(&ChannelList, cChannels);
hr = (NULL != ppEnum) ? S_OK : E_OUTOFMEMORY;
ChannelList.EmptyList();
}
return hr;
}
HRESULT CConfObject::GetChannelCount(ULONG *puCount)
{
HRESULT hr = E_POINTER;
if (NULL != puCount)
{
ULONG cChannels = 0;
if (NULL != m_pChannelAudioLocal)
{
++cChannels;
}
if (NULL != m_pChannelAudioRemote)
{
++cChannels;
}
if (NULL != m_pChannelVideoLocal)
{
++cChannels;
}
if (NULL != m_pChannelVideoRemote)
{
++cChannels;
}
*puCount = cChannels;
hr = S_OK;
}
return hr;
}
/* P M E M B E R L O C A L */
/*-------------------------------------------------------------------------
%%Function: PMemberLocal
-------------------------------------------------------------------------*/
CNmMember * PMemberLocal(COBLIST *pList)
{
if (NULL != pList)
{
POSITION posCurr;
POSITION pos = pList->GetHeadPosition();
while (NULL != pos)
{
posCurr = pos;
CNmMember * pMember = (CNmMember *) pList->GetNext(pos);
ASSERT(NULL != pMember);
if (pMember->FLocal())
return pMember;
}
}
return NULL;
}
HRESULT STDMETHODCALLTYPE CConfObject::CreateDataChannelEx(INmChannelData **ppChannel, REFGUID rguid, BYTE * pER)
{
if (NULL != ppChannel)
{
if (IsBadWritePtr(ppChannel, sizeof(LPVOID)))
return E_POINTER;
*ppChannel = NULL;
}
if (GUID_NULL == rguid)
{
WARNING_OUT(("CreateDataChannel: Null guid"));
return E_INVALIDARG;
}
{ // Make sure we're in a data conference
CNmMember * pMember = PMemberLocal(&m_MemberList);
if (NULL == pMember)
return E_FAIL;
// Data must be available
if (!FValidGccId(pMember->GetGCCID()))
return NM_E_NO_T120_CONFERENCE;
}
// Make sure the data channel has not already been created
GUID g = rguid;
POSITION pCur = m_DataChannelGUIDS.GetHeadPosition();
while(pCur)
{
GUID* pG = reinterpret_cast<GUID*>(m_DataChannelGUIDS.GetNext(pCur));
if(*pG == rguid)
{
return NM_E_CHANNEL_ALREADY_EXISTS;
}
}
CNmChannelData * pChannel = new CNmChannelData(this, rguid, (PGCCEnrollRequest) pER);
if (NULL == pChannel)
{
WARNING_OUT(("CreateChannelData: Unable to create data channel"));
return E_OUTOFMEMORY;
}
HRESULT hr = pChannel->OpenConnection();
if (FAILED(hr))
{
ERROR_OUT(("CreateDataChannel: Unable to set guid / create T.120 channels"));
// Failed to create T.120 data channels
delete pChannel;
*ppChannel = NULL;
return hr;
}
GUID* pG = new GUID;
*pG = g;
m_DataChannelGUIDS.AddTail(pG);
NotifySink((INmChannel *) pChannel, OnNotifyChannelAdded);
TRACE_OUT(("CreateChannelData: Created data channel %08X", pChannel));
// Now we are active
NotifySink((INmChannel*) pChannel, OnNotifyChannelUpdated);
if (NULL != ppChannel)
{
*ppChannel = (INmChannelData *)pChannel;
// pChannel->AddRef(); // Caller needs to release the initial lock
}
else
{
pChannel->Release(); // No one is watching this channel? - free it now
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CConfObject::CreateDataChannel(INmChannelData **ppChannel, REFGUID rguid)
{
return CreateDataChannelEx(ppChannel, rguid, NULL);
}
HRESULT CConfObject::IsHosting(void)
{
return m_fServerMode ? S_OK : S_FALSE;
}
HRESULT CConfObject::Host(void)
{
HRESULT hr = E_FAIL;
if (m_fServerMode || IsConferenceActive())
{
WARNING_OUT(("Conference already exists!"));
// ncsRet = UI_RC_CONFERENCE_ALREADY_EXISTS;
}
else
{
HRESULT ncsRet = CreateConference();
if (S_OK == ncsRet)
{
// The only success case:
TRACE_OUT(("Create local issued successfully"));
m_fServerMode = TRUE;
hr = S_OK;
}
else
{
// UI?
WARNING_OUT(("Create local failed!"));
}
}
return hr;
}
HRESULT CConfObject::Leave(void)
{
DebugEntry(CConfObject::Leave);
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
if (NULL != pOprahNCUI)
{
pOprahNCUI->CancelCalls();
}
HRESULT hr = S_OK;
switch (m_csState)
{
case CS_GOING_DOWN:
// we are already exiting
break;
case CS_COMING_UP:
case CS_RUNNING:
{
SetT120State(CS_GOING_DOWN);
ASSERT(m_hConf);
TRACE_OUT(("Calling IDataConference::Leave"));
hr = m_hConf->Leave();
if (FAILED(hr))
{
WARNING_OUT(("IDataConference::Leave failed"));
}
break;
}
default:
hr = E_FAIL;
break;
}
if(FAILED(LeaveH323(FALSE /* fKeepAV */ )))
{
// overwrite return value.... I guess this error is as good as any error
hr = E_FAIL;
}
DebugExitHRESULT(CConfObject::Leave, hr);
return hr;
}
HRESULT CConfObject::LeaveH323(BOOL fKeepAV)
{
HRESULT hrRet = S_OK;
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos && !m_MemberList.IsEmpty())
{
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
ASSERT(pMember);
HRESULT hr;
DWORD dwFlags = pMember->GetDwFlags();
IH323Endpoint * pConnection = pMember->GetH323Endpoint();
if(pConnection)
{
if (!fKeepAV || !((PF_MEDIA_AUDIO | PF_MEDIA_VIDEO) & dwFlags))
{
ConnectStateType state = CLS_Idle;
hr = pConnection->GetState(&state);
if (SUCCEEDED(hr))
{
if(state != CLS_Idle)
{
ASSERT(dwFlags & PF_H323);
hr = pConnection->Disconnect();
if (SUCCEEDED(hr))
{
TRACE_OUT(("pConnection->Disconnect() succeeded!"));
}
else
{
hrRet = E_FAIL;
WARNING_OUT(("pConnection->Disconnect() failed!"));
}
}
}
else
{
hrRet = E_FAIL;
}
}
}
}
return hrRet;
}
HRESULT CConfObject::LaunchRemote(REFGUID rguid, INmMember *pMember)
{
DWORD dwUserId = 0;
if(m_hConf)
{
if (NULL != pMember)
{
dwUserId = ((CNmMember*)pMember)->GetGCCID();
}
ASSERT(g_pNodeController);
ASSERT(m_hConf);
HRESULT nsRet = m_hConf->LaunchGuid(&rguid,
(PUINT) &dwUserId, (0 == dwUserId) ? 0 : 1);
return (S_OK == nsRet) ? S_OK : E_FAIL;
}
return NM_E_NO_T120_CONFERENCE;
}
STDMETHODIMP CConfObject::DisconnectAV(INmMember *pMember)
{
return E_FAIL;
}
/****************************************************************************
*
* CLASS: CConfObject
*
* FUNCTION: ConnectAV(LPCTSTR, LPCTSTR)
*
* PURPOSE: Switches Audio and Video to a new person (given an IP address)
*
****************************************************************************/
STDMETHODIMP CConfObject::ConnectAV(INmMember *pMember)
{
DebugEntry(CConfRoom::SwitchAV);
HRESULT hr = E_FAIL;
DebugExitHRESULT(CConfRoom::SwitchAV, hr);
return hr;
}
/****************************************************************************
*
* CLASS: CConfObject
*
* FUNCTION: GetConferenceHandle(DWORD *)
*
* PURPOSE: Gets the T120 conference handle
*
****************************************************************************/
STDMETHODIMP CConfObject::GetConferenceHandle(DWORD_PTR *pdwHandle)
{
HRESULT hr = E_FAIL;
if (NULL != pdwHandle)
{
CONF_HANDLE hConf = GetConfHandle();
*pdwHandle = (DWORD_PTR)hConf;
hr = S_OK;
}
return hr;
}
/* O N N O T I F Y S T A T E C H A N G E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyStateChanged
-------------------------------------------------------------------------*/
HRESULT OnNotifyStateChanged(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->StateChanged((NM_CONFERENCE_STATE)((DWORD_PTR)pv));
return S_OK;
}
/* O N N O T I F Y M E M B E R A D D E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyMemberAdded
-------------------------------------------------------------------------*/
HRESULT OnNotifyMemberAdded(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->MemberChanged(NM_MEMBER_ADDED, (INmMember *) pv);
return S_OK;
}
/* O N N O T I F Y M E M B E R U P D A T E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyMemberUpdated
-------------------------------------------------------------------------*/
HRESULT OnNotifyMemberUpdated(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->MemberChanged(NM_MEMBER_UPDATED, (INmMember *) pv);
return S_OK;
}
/* O N N O T I F Y M E M B E R R E M O V E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyMemberRemoved
-------------------------------------------------------------------------*/
HRESULT OnNotifyMemberRemoved(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->MemberChanged(NM_MEMBER_REMOVED, (INmMember *) pv);
return S_OK;
}
/* O N N O T I F Y C H A N N E L A D D E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyChannelAdded
-------------------------------------------------------------------------*/
HRESULT OnNotifyChannelAdded(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->ChannelChanged(NM_CHANNEL_ADDED, (INmChannel *) pv);
return S_OK;
}
/* O N N O T I F Y C H A N N E L U P D A T E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyChannelUpdated
-------------------------------------------------------------------------*/
HRESULT OnNotifyChannelUpdated(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->ChannelChanged(NM_CHANNEL_UPDATED, (INmChannel *) pv);
return S_OK;
}
/* O N N O T I F Y C H A N N E L R E M O V E D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyChannelRemoved
-------------------------------------------------------------------------*/
HRESULT OnNotifyChannelRemoved(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->ChannelChanged(NM_CHANNEL_REMOVED, (INmChannel *) pv);
return S_OK;
}
HRESULT OnNotifyStreamEvent(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
StreamEventInfo *pInfo = (StreamEventInfo*)pv;
ASSERT(NULL != pConfNotify);
HRESULT hr;
if (riid != IID_INmConferenceNotify2)
return E_NOINTERFACE;
((INmConferenceNotify2*)pConfNotify)->StreamEvent(pInfo->uEventCode, pInfo->uSubCode, pInfo->pChannel);
return S_OK;
}
/* O N N O T I F Y N M U I */
/*-------------------------------------------------------------------------
%%Function: OnNotifyNmUI
-------------------------------------------------------------------------*/
HRESULT OnNotifyNmUI(IUnknown *pConfNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pConfNotify);
((INmConferenceNotify*)pConfNotify)->NmUI((CONFN)((DWORD_PTR)pv));
return S_OK;
}
/* G E T C O N F O B J E C T */
/*-------------------------------------------------------------------------
%%Function: GetConfObject
Global function to get the conference object
-------------------------------------------------------------------------*/
CConfObject * GetConfObject(void)
{
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
if (NULL != pOprahNCUI)
{
return pOprahNCUI->GetConfObject();
}
return NULL;
}
/* G E T C O N F E R E N C E */
/*-------------------------------------------------------------------------
%%Function: GetConference
Global function to get the INmConference interface to the conf object
-------------------------------------------------------------------------*/
HRESULT GetConference(INmConference **ppConference)
{
HRESULT hr = E_POINTER;
if (NULL != ppConference)
{
hr = E_FAIL;
INmConference *pConference = GetConfObject();
if (NULL != pConference)
{
pConference->AddRef();
hr = S_OK;
}
*ppConference = pConference;
}
return hr;
}
/* G E T M E M B E R L I S T */
/*-------------------------------------------------------------------------
%%Function: GetMemberList
Global function to get the member list
-------------------------------------------------------------------------*/
COBLIST * GetMemberList(void)
{
CConfObject* pco = ::GetConfObject();
if (NULL == pco)
return NULL;
return pco->GetMemberList();
}
/* P F _ V E R _ F R O M D W */
/*-------------------------------------------------------------------------
%%Function: PF_VER_FromDw
-------------------------------------------------------------------------*/
DWORD PF_VER_FromDw(DWORD dw)
{
if (DWVERSION_NM_1 == dw)
return PF_VER_NM_1;
if ((DWVERSION_NM_2b2 <= dw) && (DWVERSION_NM_2 >= dw))
return PF_VER_NM_2;
if ((DWVERSION_NM_3a1 <= dw) && (DWVERSION_NM_3max >= dw))
return PF_VER_NM_3;
if ((DWVERSION_NM_4a1 <= dw) && (DWVERSION_NM_CURRENT >= dw))
return PF_VER_NM_4;
if (dw > DWVERSION_NM_CURRENT)
return PF_VER_FUTURE;
return PF_VER_UNKNOWN;
}
/* P F _ V E R _ F R O M U S E R D A T A */
/*-------------------------------------------------------------------------
%%Function: PV_VER_FromUserData
-------------------------------------------------------------------------*/
DWORD PF_VER_FromUserData(ROSTER_DATA_HANDLE hUserData)
{
UINT cb;
PT120PRODUCTVERSION pVersion;
PVOID pv;
static const GUID g_csguidVerInfo = GUID_VERSION;
ASSERT(NULL != g_pNodeController);
if (NULL == hUserData)
return PF_VER_UNKNOWN; // not NetMeeting
// Try to find the T.120 Product Version guid
if ((NOERROR == g_pNodeController->GetUserData(hUserData,
&g_csguidVerInfo, &cb, (PVOID *) &pVersion))
&& (cb < sizeof(T120PRODUCTVERSION)) )
{
return PF_VER_FromDw(pVersion->dwVersion);
}
// Try to extract the build number from the hex string for VER_PRODUCTVERSION_DW
if ((NOERROR == g_pNodeController->GetUserData(hUserData,
(GUID *) &g_csguidRostInfo, &cb, &pv)))
{
CRosterInfo ri;
ri.Load(pv);
TCHAR szVersion[MAX_PATH];
if (SUCCEEDED(ri.ExtractItem(NULL,
g_cwszVerTag, szVersion, CCHMAX(szVersion))))
{
return PF_VER_FromDw(DwFromHex(szVersion));
}
}
return PF_VER_NM_1; // Must be at least NetMeeting 1.0
}
DWORD CConfObject::GetDwUserIdLocal(void)
{
CNmMember * pMemberLocal = GetLocalMember();
if (NULL != pMemberLocal)
{
return pMemberLocal->GetGCCID();
}
return 0;
}
CNmMember * CConfObject::PMemberFromGCCID(UINT uNodeID)
{
COBLIST* pMemberList = ::GetMemberList();
if (NULL != pMemberList)
{
POSITION pos = pMemberList->GetHeadPosition();
while (pos)
{
CNmMember * pMember = (CNmMember *) pMemberList->GetNext(pos);
ASSERT(NULL != pMember);
if (uNodeID == pMember->GetGCCID())
{
return pMember;
}
}
}
return NULL;
}
CNmMember * CConfObject::PMemberFromNodeGuid(REFGUID rguidNode)
{
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
if (pMember->GetNodeGuid() == rguidNode)
{
return pMember;
}
}
return NULL;
}
CNmMember * CConfObject::PMemberFromH323Endpoint(IH323Endpoint * pConnection)
{
COBLIST* pMemberList = ::GetMemberList();
if (NULL != pMemberList)
{
POSITION pos = pMemberList->GetHeadPosition();
while (pos)
{
CNmMember * pMember = (CNmMember *) pMemberList->GetNext(pos);
ASSERT(NULL != pMember);
if (pConnection == pMember->GetH323Endpoint())
{
return pMember;
}
}
}
return NULL;
}
CNmMember * CConfObject::PDataMemberFromName(PCWSTR pwszName)
{
POSITION pos = m_MemberList.GetHeadPosition();
while (NULL != pos)
{
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
if(pMember->FHasData())
{
if (0 == UnicodeCompare(pwszName, pMember->GetName()))
{
return pMember;
}
}
}
return NULL;
}
// IStreamEventNotify method
// get called whenever a major event on the stream occurs
HRESULT __stdcall CConfObject::EventNotification(UINT uDirection, UINT uMediaType, UINT uEventCode, UINT uSubCode)
{
CNmChannelAudio *pChannel = NULL;
ULONG uStatus = 0;
StreamEventInfo seInfo;
if (uMediaType == MCF_AUDIO)
{
if (uDirection == MCF_SEND)
{
pChannel = m_pChannelAudioLocal;
}
else if (uDirection == MCF_RECV)
{
pChannel = m_pChannelAudioRemote;
}
}
if (pChannel)
{
// If we get a device failure notification,
// do a quick check to see if the device is indeed
// jammed. The device may have opened by the time we
// got this notification
seInfo.pChannel = pChannel;
seInfo.uSubCode = uSubCode;
switch (uEventCode)
{
case STREAM_EVENT_DEVICE_FAILURE:
{
seInfo.uEventCode = (NM_STREAMEVENT)NM_STREAMEVENT_DEVICE_FAILURE;
NotifySink((void*)&seInfo, OnNotifyStreamEvent);
break;
}
case STREAM_EVENT_DEVICE_OPEN:
{
seInfo.uEventCode = (NM_STREAMEVENT)NM_STREAMEVENT_DEVICE_OPENED;
NotifySink((void*)&seInfo, OnNotifyStreamEvent);
break;
}
default:
{
break;
}
}
}
else
{
return E_FAIL;
}
return S_OK;
}