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.
 
 
 
 
 
 

1534 lines
41 KiB

#include "precomp.h"
#include <it120app.h>
//
// CMG.C
// Call Management
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_NET
//
// CMP_Init()
//
BOOL CMP_Init(BOOL * pfCleanup)
{
BOOL rc = FALSE;
GCCError gcc_rc;
DebugEntry(CMP_Init);
UT_Lock(UTLOCK_T120);
if (g_putCMG || g_pcmPrimary)
{
*pfCleanup = FALSE;
ERROR_OUT(("Can't start CMP primary task; already running"));
DC_QUIT;
}
else
{
*pfCleanup = TRUE;
}
//
// Register CMG task
//
if (!UT_InitTask(UTTASK_CMG, &g_putCMG))
{
ERROR_OUT(("Failed to start CMG task"));
DC_QUIT;
}
//
// Allocate a Call Manager handle, ref counted
//
g_pcmPrimary = (PCM_PRIMARY)UT_MallocRefCount(sizeof(CM_PRIMARY), TRUE);
if (!g_pcmPrimary)
{
ERROR_OUT(("CMP_Init failed to allocate CM_PRIMARY data"));
DC_QUIT;
}
SET_STAMP(g_pcmPrimary, CMPRIMARY);
g_pcmPrimary->putTask = g_putCMG;
//
// Init the people list
//
COM_BasedListInit(&(g_pcmPrimary->people));
//
// Register event and exit procedures
//
UT_RegisterExit(g_putCMG, CMPExitProc, g_pcmPrimary);
g_pcmPrimary->exitProcRegistered = TRUE;
//
// - GCCCreateSap, which is the interesting one.
//
gcc_rc = GCC_CreateAppSap((IGCCAppSap **) &(g_pcmPrimary->pIAppSap),
g_pcmPrimary,
CMPGCCCallback);
if (GCC_NO_ERROR != gcc_rc || NULL == g_pcmPrimary->pIAppSap)
{
ERROR_OUT(( "Error from GCCCreateSap"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
UT_Unlock(UTLOCK_T120);
DebugExitBOOL(CMP_Init, rc);
return(rc);
}
//
// CMP_Term()
//
void CMP_Term(void)
{
DebugEntry(CMP_Term);
UT_Lock(UTLOCK_T120);
if (g_pcmPrimary)
{
ValidateCMP(g_pcmPrimary);
ValidateUTClient(g_putCMG);
//
// Unregister our GCC SAP.
//
if (NULL != g_pcmPrimary->pIAppSap)
{
g_pcmPrimary->pIAppSap->ReleaseInterface();
g_pcmPrimary->pIAppSap = NULL;
}
//
// Call the exit procedure to do all our termination
//
CMPExitProc(g_pcmPrimary);
}
UT_TermTask(&g_putCMG);
UT_Unlock(UTLOCK_T120);
DebugExitVOID(CMP_Term);
}
//
// CMPExitProc()
//
void CALLBACK CMPExitProc(LPVOID data)
{
PCM_PRIMARY pcmPrimary = (PCM_PRIMARY)data;
DebugEntry(CMPExitProc);
UT_Lock(UTLOCK_T120);
//
// Check parameters
//
ValidateCMP(pcmPrimary);
ASSERT(pcmPrimary == g_pcmPrimary);
//
// Deregister the exit procedure.
//
if (pcmPrimary->exitProcRegistered)
{
UT_DeregisterExit(pcmPrimary->putTask,
CMPExitProc,
pcmPrimary);
pcmPrimary->exitProcRegistered = FALSE;
}
CMPCallEnded(pcmPrimary);
//
// Free the CMP data
//
UT_FreeRefCount((void**)&g_pcmPrimary, TRUE);
UT_Unlock(UTLOCK_T120);
DebugExitVOID(CMPExitProc);
}
//
// CMPCallEnded()
//
void CMPCallEnded
(
PCM_PRIMARY pcmPrimary
)
{
PCM_PERSON pPerson;
PCM_PERSON pPersonT;
int cmTask;
DebugEntry(CMPCallEnded);
ValidateCMP(pcmPrimary);
if (!(pcmPrimary->currentCall))
{
TRACE_OUT(("CMCallEnded: not in call"));
DC_QUIT;
}
//
// Issue CMS_PERSON_LEFT events for all people still in the call.
// Do this back to front.
//
pPerson = (PCM_PERSON)COM_BasedListLast(&(pcmPrimary->people), FIELD_OFFSET(CM_PERSON, chain));
while (pPerson != NULL)
{
ASSERT(pcmPrimary->peopleCount > 0);
TRACE_OUT(("Person [%d] LEAVING call", pPerson->netID));
//
// Get the previous person
//
pPersonT = (PCM_PERSON)COM_BasedListPrev(&(pcmPrimary->people), pPerson,
FIELD_OFFSET(CM_PERSON, chain));
//
// Remove this guy from the list
//
COM_BasedListRemove(&(pPerson->chain));
pcmPrimary->peopleCount--;
//
// Notify people of his leaving
//
CMPBroadcast(pcmPrimary,
CMS_PERSON_LEFT,
pcmPrimary->peopleCount,
pPerson->netID);
//
// Free the memory for the item
//
delete pPerson;
//
// Move the previous person in the list
pPerson = pPersonT;
}
//
// Inform all registered secondary tasks of call ending (call
// CMbroadcast() with CMS_END_CALL)
//
CMPBroadcast(pcmPrimary,
CMS_END_CALL,
0,
pcmPrimary->callID);
//
// Reset the current call vars
//
pcmPrimary->currentCall = FALSE;
pcmPrimary->fTopProvider = FALSE;
pcmPrimary->callID = 0;
pcmPrimary->gccUserID = 0;
pcmPrimary->gccTopProviderID = 0;
//
// Discard outstanding channel/token requests
//
for (cmTask = CMTASK_FIRST; cmTask < CMTASK_MAX; cmTask++)
{
if (pcmPrimary->tasks[cmTask])
{
pcmPrimary->tasks[cmTask]->channelKey = 0;
pcmPrimary->tasks[cmTask]->tokenKey = 0;
}
}
DC_EXIT_POINT:
//
// Nobody should be in the call anymore
//
ASSERT(pcmPrimary->peopleCount == 0);
DebugExitVOID(CMCallEnded);
}
//
// CMPGCCCallback
//
void CALLBACK CMPGCCCallback(GCCAppSapMsg * gccMessage)
{
PCM_PRIMARY pcmPrimary;
GCCConferenceID confID;
GCCApplicationRoster FAR * FAR * pRosterList;
UINT roster;
LPOSTR pOctetString;
GCCObjectKey FAR * pObjectKey;
UINT checkLen;
DebugEntry(CMPGCCCallback);
UT_Lock(UTLOCK_T120);
//
// The userDefined parameter is the Primary's PCM_CLIENT.
//
pcmPrimary = (PCM_PRIMARY)gccMessage->pAppData;
if (pcmPrimary != g_pcmPrimary)
{
ASSERT(NULL == g_pcmPrimary);
return;
}
ValidateCMP(pcmPrimary);
switch (gccMessage->eMsgType)
{
case GCC_PERMIT_TO_ENROLL_INDICATION:
{
//
// This indicates a conference has started:
//
CMPProcessPermitToEnroll(pcmPrimary,
&gccMessage->AppPermissionToEnrollInd);
}
break;
case GCC_ENROLL_CONFIRM:
{
//
// This contains the result of a GCCApplicationEnrollRequest.
//
CMPProcessEnrollConfirm(pcmPrimary,
&gccMessage->AppEnrollConfirm);
}
break;
case GCC_REGISTER_CHANNEL_CONFIRM:
{
//
// This contains the result of a GCCRegisterChannelRequest.
//
CMPProcessRegistryConfirm(
pcmPrimary,
gccMessage->eMsgType,
&gccMessage->RegistryConfirm);
}
break;
case GCC_ASSIGN_TOKEN_CONFIRM:
{
//
// This contains the result of a GCCRegistryAssignTokenRequest.
//
CMPProcessRegistryConfirm(
pcmPrimary,
gccMessage->eMsgType,
&gccMessage->RegistryConfirm);
}
break;
case GCC_APP_ROSTER_REPORT_INDICATION:
{
//
// This indicates that the application roster has changed.
//
confID = gccMessage->AppRosterReportInd.nConfID;
pRosterList = gccMessage->AppRosterReportInd.apAppRosters;
for (roster = 0;
roster < gccMessage->AppRosterReportInd.cRosters;
roster++)
{
//
// Check this app roster to see if it relates to the
// Groupware session (the first check is because we always
// use a NON_STANDARD application key).
//
pObjectKey = &(pRosterList[roster]->
session_key.application_protocol_key);
//
// We only ever use a non standard key.
//
if (pObjectKey->key_type != GCC_H221_NONSTANDARD_KEY)
{
TRACE_OUT(("Standard key, so not a roster we are interested in..."));
continue;
}
pOctetString = &pObjectKey->h221_non_standard_id;
//
// Now check the octet string. It should be the same
// length as our hardcoded GROUPWARE- string (including
// NULL term) and should match byte for byte:
//
checkLen = sizeof(GROUPWARE_GCC_APPLICATION_KEY);
if ((pOctetString->length != checkLen)
||
(memcmp(pOctetString->value,
GROUPWARE_GCC_APPLICATION_KEY,
checkLen) != 0))
{
//
// This roster is not for our session - go to the next
// one.
//
TRACE_OUT(("Roster not for Groupware session - ignore"));
continue;
}
//
// Process the application roster.
//
CMPProcessAppRoster(pcmPrimary,
confID,
pRosterList[roster]);
}
}
break;
}
UT_Unlock(UTLOCK_T120);
DebugExitVOID(CMPGCCCallback);
}
//
//
// CMPBuildGCCRegistryKey(...)
//
//
void CMPBuildGCCRegistryKey
(
UINT dcgKeyNum,
GCCRegistryKey FAR * pGCCKey,
LPSTR dcgKeyStr
)
{
DebugEntry(CMPBuildGCCRegistryKey);
//
// Build up a string of the form "Groupware-XX" where XX is a string
// representation (in decimal) of the <dcgKey> parameter passed in.
//
memcpy(dcgKeyStr, GROUPWARE_GCC_APPLICATION_KEY, sizeof(GROUPWARE_GCC_APPLICATION_KEY)-1);
wsprintf(dcgKeyStr+sizeof(GROUPWARE_GCC_APPLICATION_KEY)-1, "%d",
dcgKeyNum);
//
// Now build the GCCRegistryKey. This involves putting a pointer to
// our static <dcgKeyStr> deep inside the GCC structure. We also store
// the length, which is lstrlen+1, because we want to include the
// NULLTERM explicitly (since GCC treats the octet_string as an
// arbitrary array of bytes).
//
pGCCKey->session_key.application_protocol_key.
key_type = GCC_H221_NONSTANDARD_KEY;
pGCCKey->session_key.application_protocol_key.h221_non_standard_id.
length = sizeof(GROUPWARE_GCC_APPLICATION_KEY);
pGCCKey->session_key.application_protocol_key.h221_non_standard_id.
value = (LPBYTE) GROUPWARE_GCC_APPLICATION_KEY;
pGCCKey->session_key.session_id = 0;
pGCCKey->resource_id.length =
(sizeof(GROUPWARE_GCC_APPLICATION_KEY) +
lstrlen(&dcgKeyStr[sizeof(GROUPWARE_GCC_APPLICATION_KEY)-1]));
pGCCKey->resource_id.value = (LPBYTE) dcgKeyStr;
DebugExitVOID(CMPBuildGCCRegistryKey);
}
//
// CMPProcessPermitToEnroll(...)
//
void CMPProcessPermitToEnroll
(
PCM_PRIMARY pcmPrimary,
GCCAppPermissionToEnrollInd * pMsg
)
{
DebugEntry(CMPProcessPermitToEnroll);
ValidateCMP(pcmPrimary);
//
// We will send CMS_PERSON_JOINED events when we receive a
// GCC_APP_ROSTER_REPORT_INDICATION.
//
if (pMsg->fPermissionGranted)
{
// CALL STARTED
//
// If we haven't had a NCS yet then we store the conference ID.
// Otherwise ignore it.
//
ASSERT(!pcmPrimary->currentCall);
//
// Initially, we do not consider ourselves to be in the call - we will
// add an entry when we get the ENROLL_CONFIRM:
//
ASSERT(pcmPrimary->peopleCount == 0);
pcmPrimary->currentCall = TRUE;
pcmPrimary->callID = pMsg->nConfID;
pcmPrimary->fTopProvider =
pcmPrimary->pIAppSap->IsThisNodeTopProvider(pMsg->nConfID);
//
// Tell GCC whether we're interested:
//
if (!CMPGCCEnroll(pcmPrimary, pMsg->nConfID, TRUE))
{
//
// We are only interested in an error if it is a Groupware conf.
// All we can really do is pretend the conference has ended due
// to a network error.
//
WARNING_OUT(("Error from CMPGCCEnroll"));
CMPCallEnded(pcmPrimary);
}
//
// The reply will arrive on a GCC_ENROLL_CONFIRM event.
//
}
else
{
// CALL ENDED
if (g_pcmPrimary->currentCall)
{
//
// Inform Primary task and all secondary tasks that the call has ended
//
CMPCallEnded(g_pcmPrimary);
//
// Un-enroll from the GCC Application Roster.
//
if (g_pcmPrimary->bGCCEnrolled)
{
CMPGCCEnroll(g_pcmPrimary, g_pcmPrimary->callID, FALSE);
g_pcmPrimary->bGCCEnrolled = FALSE;
}
}
}
DebugExitVOID(CMPProcessPermitToEnroll);
}
//
//
// CMPProcessEnrollConfirm(...)
//
//
void CMPProcessEnrollConfirm
(
PCM_PRIMARY pcmPrimary,
GCCAppEnrollConfirm * pMsg
)
{
DebugEntry(CMPProcessEnrollConfirm);
ValidateCMP(pcmPrimary);
ASSERT(pcmPrimary->currentCall);
ASSERT(pMsg->nConfID == pcmPrimary->callID);
//
// This event contains the GCC node ID (i.e. the MCS user ID of the
// GCC node controller at this node). Store it for later reference
// against the roster report:
//
TRACE_OUT(( "GCC user_id: %u", pMsg->nidMyself));
pcmPrimary->gccUserID = pMsg->nidMyself;
pcmPrimary->gccTopProviderID = pcmPrimary->pIAppSap->GetTopProvider(pcmPrimary->callID);
ASSERT(pcmPrimary->gccTopProviderID);
if (pMsg->nResult != GCC_RESULT_SUCCESSFUL)
{
WARNING_OUT(( "Attempt to enroll failed (reason: %u", pMsg->nResult));
//
// All we can really do is pretend the conference has ended due to
// a network error.
//
CMPCallEnded(pcmPrimary);
}
DebugExitVOID(CMProcessEnrollConfirm);
}
//
// CMPProcessRegistryConfirm(...)
//
void CMPProcessRegistryConfirm
(
PCM_PRIMARY pcmPrimary,
GCCMessageType messageType,
GCCRegistryConfirm *pConfirm
)
{
UINT event = 0;
BOOL succeeded;
LPSTR pGCCKeyStr; // extracted from the GCC registry key
UINT dcgKeyNum; // the value originally passed in as key
UINT itemID; // can be channel or token ID
int cmTask;
PUT_CLIENT secondaryHandle = NULL;
DebugEntry(CMPProcessRegistryConfirm);
ValidateCMP(pcmPrimary);
//
// Check this is for the Groupware conference:
//
if (!pcmPrimary->currentCall ||
(pConfirm->nConfID != pcmPrimary->callID))
{
WARNING_OUT(( "Got REGISTRY_XXX_CONFIRM for unknown conference %lu",
pConfirm->nConfID));
DC_QUIT;
}
//
// Embedded deep down inside the message from GCC is a pointer to an
// octet string which is of the form "Groupware-XX", where XX is a
// string representation of the numeric key the original Call Manager
// secondary used when registering the item. Extract it now:
//
pGCCKeyStr = (LPSTR)pConfirm->pRegKey->resource_id.value;
dcgKeyNum = DecimalStringToUINT(&pGCCKeyStr[sizeof(GROUPWARE_GCC_APPLICATION_KEY)-1]);
if (dcgKeyNum == 0)
{
WARNING_OUT(( "Received ASSIGN/REGISTER_CONFIRM with unknown key: %s",
pGCCKeyStr));
DC_QUIT;
}
TRACE_OUT(( "Conf ID %u, DCG Key %u, result %u",
pConfirm->nConfID, dcgKeyNum, pConfirm->nResult));
//
// This is either a REGISTER_CHANNEL_CONFIRM or a ASSIGN_TOKEN_CONFIRM.
// Check, and set up the relevant pointers:
//
switch (messageType)
{
case GCC_REGISTER_CHANNEL_CONFIRM:
{
event = CMS_CHANNEL_REGISTER_CONFIRM;
itemID = pConfirm->pRegItem->channel_id;
// Look for task that registered this channel
for (cmTask = CMTASK_FIRST; cmTask < CMTASK_MAX; cmTask++)
{
if (pcmPrimary->tasks[cmTask] &&
(pcmPrimary->tasks[cmTask]->channelKey == dcgKeyNum))
{
pcmPrimary->tasks[cmTask]->channelKey = 0;
secondaryHandle = pcmPrimary->tasks[cmTask]->putTask;
}
}
}
break;
case GCC_ASSIGN_TOKEN_CONFIRM:
{
event = CMS_TOKEN_ASSIGN_CONFIRM;
itemID = pConfirm->pRegItem->token_id;
// Look for task that assigned this token
for (cmTask = CMTASK_FIRST; cmTask < CMTASK_MAX; cmTask++)
{
if (pcmPrimary->tasks[cmTask] &&
(pcmPrimary->tasks[cmTask]->tokenKey == dcgKeyNum))
{
pcmPrimary->tasks[cmTask]->tokenKey = 0;
secondaryHandle = pcmPrimary->tasks[cmTask]->putTask;
}
}
}
break;
default:
{
ERROR_OUT(( "Unexpected registry event %u", messageType));
DC_QUIT;
}
}
switch (pConfirm->nResult)
{
case GCC_RESULT_SUCCESSFUL:
{
//
// We were the first to register an item against this key.
//
TRACE_OUT(("We were first to register using key %u (itemID: %u)",
dcgKeyNum, itemID));
succeeded = TRUE;
}
break;
case GCC_RESULT_ENTRY_ALREADY_EXISTS:
{
//
// Someone beat us to it: they have registered a channel
// against the key we specified. This value is in the GCC
// message:
//
TRACE_OUT(("Another node registered using key %u (itemID: %u)",
dcgKeyNum, itemID));
succeeded = TRUE;
}
break;
default:
{
ERROR_OUT(("Error %#hx registering/assigning item against key %u",
pConfirm->nResult, dcgKeyNum));
succeeded = FALSE;
}
break;
}
//
// Tell the secondary about the result.
//
if (secondaryHandle)
{
UT_PostEvent(pcmPrimary->putTask,
secondaryHandle,
0,
event,
succeeded,
MAKELONG(itemID, dcgKeyNum));
}
DC_EXIT_POINT:
DebugExitVOID(CMProcessRegistryConfirm);
}
//
// CMPProcessAppRoster(...)
//
void CMPProcessAppRoster
(
PCM_PRIMARY pcmPrimary,
GCCConferenceID confID,
GCCApplicationRoster* pAppRoster
)
{
UINT newList;
UserID oldNode;
UserID newNode;
PCM_PERSON pPerson;
PCM_PERSON pPersonT;
BOOL found;
int task;
BOOL notInOldRoster = TRUE;
BOOL inNewRoster = FALSE;
DebugEntry(CMPProcessAppRoster);
ValidateCMP(pcmPrimary);
//
// If we are not in a call ignore this.
//
if (!pcmPrimary->currentCall ||
(confID != pcmPrimary->callID))
{
WARNING_OUT(("Report not for active Groupware conference - ignore"));
DC_QUIT;
}
//
// At this point, pAppRoster points to the bit of the roster which
// relates to Groupware. Trace out some info:
//
TRACE_OUT(( "Number of records %u;", pAppRoster->number_of_records));
TRACE_OUT(( "Nodes added: %s, removed: %s",
(pAppRoster->nodes_were_added ? "YES" : "NO"),
(pAppRoster->nodes_were_removed ? "YES" : "NO")));
//
// We store the GCC user IDs in shared memory as TSHR_PERSONIDs.
// Compare this list of people we know to be in the call, and
// * Remove people no longer around
// * See if we are new to the roster
// * Add people who are new
//
pPerson = (PCM_PERSON)COM_BasedListFirst(&(pcmPrimary->people), FIELD_OFFSET(CM_PERSON, chain));
while (pPerson != NULL)
{
ASSERT(pcmPrimary->peopleCount > 0);
oldNode = (UserID)pPerson->netID;
//
// Get the next guy in the list in case we remove this one.
//
pPersonT = (PCM_PERSON)COM_BasedListNext(&(pcmPrimary->people), pPerson,
FIELD_OFFSET(CM_PERSON, chain));
//
// Check to see if our node is currently in the roster
//
if (oldNode == pcmPrimary->gccUserID)
{
TRACE_OUT(( "We are currently in the app roster"));
notInOldRoster = FALSE;
}
//
// ...check if they're in the new list...
//
found = FALSE;
for (newList = 0; newList < pAppRoster->number_of_records; newList++)
{
if (oldNode == pAppRoster->application_record_list[newList]->node_id)
{
found = TRUE;
break;
}
}
if (!found)
{
//
// This node is no longer present, so remove him.
//
TRACE_OUT(("Person %u left", oldNode));
COM_BasedListRemove(&(pPerson->chain));
pcmPrimary->peopleCount--;
CMPBroadcast(pcmPrimary,
CMS_PERSON_LEFT,
pcmPrimary->peopleCount,
oldNode);
//
// Free the memory for the person item
//
delete pPerson;
}
pPerson = pPersonT;
}
//
// Now see if we are new to the roster
//
for (newList = 0; newList < pAppRoster->number_of_records; newList++)
{
if (pAppRoster->application_record_list[newList]->node_id ==
pcmPrimary->gccUserID)
{
TRACE_OUT(( "We are in the new app roster"));
inNewRoster = TRUE;
break;
}
}
if (notInOldRoster && inNewRoster)
{
//
// We are new to the roster so we can now do all the processing we
// were previously doing in the enroll confirm handler. GCC spec
// requires that we do not do this until we get the roster
// notification back.
//
// Flag we are enrolled and start registering channels etc.
//
pcmPrimary->bGCCEnrolled = TRUE;
//
// Post a CMS_NEW_CALL events to all secondary tasks
//
TRACE_OUT(( "Broadcasting CMS_NEW_CALL with call handle 0x%08lx",
pcmPrimary->callID));
//
// If we are not the caller then delay the broadcast a little
//
CMPBroadcast(pcmPrimary, CMS_NEW_CALL,
pcmPrimary->fTopProvider, pcmPrimary->callID);
#ifdef _DEBUG
//
// Process any outstanding channel register and assign token
// requests.
//
for (task = CMTASK_FIRST; task < CMTASK_MAX; task++)
{
if (pcmPrimary->tasks[task] != NULL)
{
ASSERT(pcmPrimary->tasks[task]->channelKey == 0);
ASSERT(pcmPrimary->tasks[task]->tokenKey == 0);
}
}
#endif // _DEBUG
}
//
// If we are not yet enrolled in the conference then do not start
// sending PERSON_JOINED notifications.
//
if (!pcmPrimary->bGCCEnrolled)
{
DC_QUIT;
}
//
// Add the new people (this will include us). At this point, we know
// that everyone in the people list is currently in the roster, since
// we would have removed 'em above.
//
// We need to walk the existing list over and over.
// But at least we can skip the people we add. So we save the current
// front of the list.
//
pPersonT = (PCM_PERSON)COM_BasedListFirst(&(pcmPrimary->people), FIELD_OFFSET(CM_PERSON, chain));
for (newList = 0; newList < pAppRoster->number_of_records; newList++)
{
newNode = pAppRoster->application_record_list[newList]->node_id;
found = FALSE;
pPerson = pPersonT;
while (pPerson != NULL)
{
if (newNode == pPerson->netID)
{
//
// This person already existed - don't need to do anything
//
found = TRUE;
break; // out of inner for loop
}
pPerson = (PCM_PERSON)COM_BasedListNext(&(pcmPrimary->people), pPerson,
FIELD_OFFSET(CM_PERSON, chain));
}
if (!found)
{
//
// This dude is new; add him to our people list.
//
TRACE_OUT(("Person with GCC user_id %u joined", newNode));
pPerson = new CM_PERSON;
if (!pPerson)
{
//
// Uh oh; can't add him.
//
ERROR_OUT(("Can't add person GCC user_id %u; out of memory",
newNode));
break;
}
ZeroMemory(pPerson, sizeof(*pPerson));
pPerson->netID = newNode;
//
// LONCHANC: We should collapse all these events into a single one
// that summarize all added and removed nodes,
// instead of posting the events one by one.
//
//
// Stick him in at the beginning. At least that way we don't
// have to look at his record anymore.
//
COM_BasedListInsertAfter(&(pcmPrimary->people), &pPerson->chain);
pcmPrimary->peopleCount++;
CMPBroadcast(pcmPrimary,
CMS_PERSON_JOINED,
pcmPrimary->peopleCount,
newNode);
}
}
TRACE_OUT(( "Num people now in call %u", pcmPrimary->peopleCount));
DC_EXIT_POINT:
DebugExitVOID(CMPProcessAppRoster);
}
//
// CMPBroadcast()
//
void CMPBroadcast
(
PCM_PRIMARY pcmPrimary,
UINT event,
UINT param1,
UINT param2
)
{
int task;
DebugEntry(CMPBroadcast);
ValidateCMP(pcmPrimary);
//
// for every secondary task
//
for (task = CMTASK_FIRST; task < CMTASK_MAX; task++)
{
if (pcmPrimary->tasks[task] != NULL)
{
UT_PostEvent(pcmPrimary->putTask,
pcmPrimary->tasks[task]->putTask,
NO_DELAY,
event,
param1,
param2);
}
}
DebugExitVOID(CMPBroadcast);
}
//
// CMPGCCEnroll(...)
//
BOOL CMPGCCEnroll
(
PCM_PRIMARY pcmPrimary,
GCCConferenceID conferenceID,
BOOL fEnroll
)
{
GCCError rcGCC = GCC_NO_ERROR;
GCCSessionKey gccSessionKey;
GCCObjectKey FAR * pGCCObjectKey;
BOOL succeeded = TRUE;
GCCEnrollRequest er;
GCCRequestTag nReqTag;
DebugEntry(CMPGCCEnroll);
ValidateCMP(pcmPrimary);
//
// Do some error checking.
//
if (fEnroll && pcmPrimary->bGCCEnrolled)
{
WARNING_OUT(("Already enrolled"));
DC_QUIT;
}
TRACE_OUT(("CMGCCEnroll for CM_hnd 0x%08x, confID 0x%08x, in/out %d",
pcmPrimary, conferenceID, fEnroll));
//
// Set up the session key which identifies us uniquely in the GCC
// AppRoster. We use a non-standard key (because we're not part of the
// T.120 standards series)
//
// Octet strings aren't null terminated, but we want ours to include
// the NULL at the end of the C string, so specify lstrlen+1 for the
// length.
//
pGCCObjectKey = &(gccSessionKey.application_protocol_key);
pGCCObjectKey->key_type = GCC_H221_NONSTANDARD_KEY;
pGCCObjectKey->h221_non_standard_id.value =
(LPBYTE) GROUPWARE_GCC_APPLICATION_KEY;
pGCCObjectKey->h221_non_standard_id.length =
sizeof(GROUPWARE_GCC_APPLICATION_KEY);
gccSessionKey.session_id = 0;
//
// Try to enroll/unenroll with GCC. This may fail because we have not
// yet received a GCC_PERMIT_TO_ENROLL_INDICATION.
//
// Fill in the enroll request structure
//
ZeroMemory(&er, sizeof(er));
er.pSessionKey = &gccSessionKey;
// er.fEnrollActively = FALSE;
// er.nUserID = 0; // no user ID
// er.fConductingCapable = FALSE;
er.nStartupChannelType = MCS_STATIC_CHANNEL;
// er.cNonCollapsedCaps = 0;
// er.apNonCollapsedCaps = NULL;
// er.cCollapsedCaps = 0;
// er.apCollapsedCaps = NULL;
er.fEnroll = fEnroll;
rcGCC = pcmPrimary->pIAppSap->AppEnroll(
conferenceID,
&er,
&nReqTag);
if (GCC_NO_ERROR != rcGCC)
{
//
// Leave the decision about any error processing to the caller.
//
TRACE_OUT(("Error 0x%08x from GCCApplicationEnrollRequest conf ID %lu enroll=%s",
rcGCC, conferenceID, fEnroll ? "YES": "NO"));
succeeded = FALSE;
}
else
{
//
// Whether we have asked to enroll or un-enroll, we act as if we
// are no longer enrolled at once. We are only really enrolled
// when we receive an enroll confirm event.
//
pcmPrimary->bGCCEnrolled = FALSE;
ASSERT(succeeded);
TRACE_OUT(( "%s with conference %d", fEnroll ?
"Enroll Outstanding" : "Unenrolled",
conferenceID));
}
DC_EXIT_POINT:
DebugExitBOOL(CMPGCCEnroll, succeeded);
return(succeeded);
}
//
// CMS_Register()
//
BOOL CMS_Register
(
PUT_CLIENT putTask,
CMTASK taskType,
PCM_CLIENT* ppcmClient
)
{
BOOL fRegistered = FALSE;
PCM_CLIENT pcmClient = NULL;
DebugEntry(CMS_Register);
UT_Lock(UTLOCK_T120);
if (!g_pcmPrimary)
{
ERROR_OUT(("CMS_Register failed; primary doesn't exist"));
DC_QUIT;
}
ValidateUTClient(putTask);
ASSERT(taskType >= CMTASK_FIRST);
ASSERT(taskType < CMTASK_MAX);
*ppcmClient = NULL;
//
// Is this task already present? If so, share it
//
if (g_pcmPrimary->tasks[taskType] != NULL)
{
TRACE_OUT(("Sharing CMS task 0x%08x", g_pcmPrimary->tasks[taskType]));
*ppcmClient = g_pcmPrimary->tasks[taskType];
ValidateCMS(*ppcmClient);
(*ppcmClient)->useCount++;
// Return -- we exist.
fRegistered = TRUE;
DC_QUIT;
}
//
// If we got here the task is not a Call Manager Secondary yet, so go
// ahead with the registration.
//
//
// Allocate memory for the client
//
pcmClient = new CM_CLIENT;
if (! pcmClient)
{
ERROR_OUT(("Could not allocate CM handle"));
DC_QUIT;
}
ZeroMemory(pcmClient, sizeof(*pcmClient));
*ppcmClient = pcmClient;
//
// Fill in information
//
SET_STAMP(pcmClient, CMCLIENT);
pcmClient->putTask = putTask;
pcmClient->taskType = taskType;
pcmClient->useCount = 1;
UT_BumpUpRefCount(g_pcmPrimary);
g_pcmPrimary->tasks[taskType] = pcmClient;
//
// Register an exit procedure
//
UT_RegisterExit(putTask, CMSExitProc, pcmClient);
pcmClient->exitProcRegistered = TRUE;
fRegistered = TRUE;
DC_EXIT_POINT:
UT_Unlock(UTLOCK_T120);
DebugExitBOOL(CMS_Register, fRegistered);
return(fRegistered);
}
//
// CMS_Deregister()
//
void CMS_Deregister(PCM_CLIENT * ppcmClient)
{
PCM_CLIENT pcmClient = *ppcmClient;
DebugEntry(CMS_Deregister);
//
// Check the parameters are valid
//
UT_Lock(UTLOCK_T120);
ValidateCMS(pcmClient);
//
// Only actually deregister the client if the registration count has
// reached zero.
//
pcmClient->useCount--;
if (pcmClient->useCount != 0)
{
DC_QUIT;
}
//
// Call the exit procedure to do our local cleanup
//
CMSExitProc(pcmClient);
DC_EXIT_POINT:
*ppcmClient = NULL;
UT_Unlock(UTLOCK_T120);
DebugExitVOID(CMS_Deregister);
}
//
// CMS_ChannelRegister()
//
BOOL CMS_ChannelRegister
(
PCM_CLIENT pcmClient,
UINT channelKey,
UINT channelID
)
{
BOOL fRegistered = FALSE;
GCCRegistryKey gccRegistryKey;
GCCError rcGCC;
char dcgKeyStr[sizeof(GROUPWARE_GCC_APPLICATION_KEY)+MAX_ITOA_LENGTH];
DebugEntry(CMS_ChannelRegister);
UT_Lock(UTLOCK_T120);
//
// Check the CMG task
//
ValidateUTClient(g_putCMG);
//
// Check the parameters are valid
//
ValidateCMP(g_pcmPrimary);
ValidateCMS(pcmClient);
//
// If we are not in a call it is an error.
//
if (!g_pcmPrimary->currentCall)
{
WARNING_OUT(("CMS_ChannelRegister failed; not in call"));
DC_QUIT;
}
if (!g_pcmPrimary->bGCCEnrolled)
{
WARNING_OUT(("CMS_ChannelRegister failed; not enrolled in call"));
DC_QUIT;
}
// Make sure we don't have one pending already
ASSERT(pcmClient->channelKey == 0);
TRACE_OUT(("Channel ID %u Key %u", channelID, channelKey));
//
// Build a GCCRegistryKey based on our channelKey:
//
CMPBuildGCCRegistryKey(channelKey, &gccRegistryKey, dcgKeyStr);
//
// Now call through to GCC. GCC will invoke our callback when it
// has processed the request.
//
rcGCC = g_pcmPrimary->pIAppSap->RegisterChannel(
g_pcmPrimary->callID,
&gccRegistryKey,
(ChannelID)channelID);
if (rcGCC)
{
//
// Tell the secondary client that the request failed.
//
WARNING_OUT(( "Error %#lx from GCCRegisterChannel (key: %u)",
rcGCC, channelKey));
}
else
{
// Remember so we can post confirm event back to proper task
pcmClient->channelKey = channelKey;
fRegistered = TRUE;
}
DC_EXIT_POINT:
UT_Unlock(UTLOCK_T120);
DebugExitBOOL(CMS_ChannelRegister, fRegistered);
return(fRegistered);
}
//
// CMS_AssignTokenId()
//
BOOL CMS_AssignTokenId
(
PCM_CLIENT pcmClient,
UINT tokenKey
)
{
GCCRegistryKey gccRegistryKey;
GCCError rcGCC;
char dcgKeyStr[sizeof(GROUPWARE_GCC_APPLICATION_KEY)+MAX_ITOA_LENGTH];
BOOL fAssigned = FALSE;
DebugEntry(CMS_AssignTokenId);
UT_Lock(UTLOCK_T120);
//
// Check the parameters are valid
//
ValidateCMP(g_pcmPrimary);
ValidateCMS(pcmClient);
ValidateUTClient(g_putCMG);
if (!g_pcmPrimary->currentCall)
{
WARNING_OUT(("CMS_AssignTokenId failing; not in call"));
DC_QUIT;
}
if (!g_pcmPrimary->bGCCEnrolled)
{
WARNING_OUT(("CMS_AssignTokenId failing; not enrolled in call"));
DC_QUIT;
}
// Make sure we don't have one already
ASSERT(pcmClient->tokenKey == 0);
//
// Build a GCCRegistryKey based on our tokenKey:
//
CMPBuildGCCRegistryKey(tokenKey, &gccRegistryKey, dcgKeyStr);
//
// Now call through to GCC. GCC will invoke our callback when it
// has processed the request.
//
rcGCC = g_pcmPrimary->pIAppSap->RegistryAssignToken(
g_pcmPrimary->callID, &gccRegistryKey);
if (rcGCC)
{
//
// Tell the secondary client that the request failed.
//
WARNING_OUT(( "Error %x from GCCAssignToken (key: %u)",
rcGCC, tokenKey));
}
else
{
// Remember so we can post confirm to proper task
pcmClient->tokenKey = tokenKey;
fAssigned = TRUE;
}
DC_EXIT_POINT:
UT_Unlock(UTLOCK_T120);
DebugExitBOOL(CMS_AssignTokenId, fAssigned);
return(fAssigned);
}
//
// CMSExitProc()
//
void CALLBACK CMSExitProc(LPVOID data)
{
PCM_CLIENT pcmClient = (PCM_CLIENT)data;
DebugEntry(CMSExitProc);
UT_Lock(UTLOCK_T120);
//
// Check parameters
//
ValidateCMS(pcmClient);
//
// Deregister exit procedure
//
if (pcmClient->exitProcRegistered)
{
UT_DeregisterExit(pcmClient->putTask,
CMSExitProc,
pcmClient);
pcmClient->exitProcRegistered = FALSE;
}
//
// Remove the task entry from the primary's list
//
g_pcmPrimary->tasks[pcmClient->taskType] = NULL;
UT_FreeRefCount((void**)&g_pcmPrimary, TRUE);
//
// Free the client data
//
delete pcmClient;
UT_Unlock(UTLOCK_T120);
DebugExitVOID(CMSExitProc);
}
BOOL WINAPI CMS_GetStatus(PCM_STATUS pcmStatus)
{
BOOL inCall;
DebugEntry(CMS_GetStatus);
UT_Lock(UTLOCK_T120);
ASSERT(!IsBadWritePtr(pcmStatus, sizeof(CM_STATUS)));
ZeroMemory(pcmStatus, sizeof(CM_STATUS));
ValidateCMP(g_pcmPrimary);
T120_GetNodeName(pcmStatus->localName, CCHMAX(pcmStatus->localName));
pcmStatus->localHandle = g_pcmPrimary->gccUserID;
pcmStatus->peopleCount = g_pcmPrimary->peopleCount;
pcmStatus->fTopProvider = g_pcmPrimary->fTopProvider;
pcmStatus->topProviderID = g_pcmPrimary->gccTopProviderID;
//
// Fill in information about other primary
//
pcmStatus->callID = g_pcmPrimary->callID;
inCall = (g_pcmPrimary->currentCall != FALSE);
UT_Unlock(UTLOCK_T120);
DebugExitBOOL(CMS_GetStatus, inCall);
return(inCall);
}