#include "precomp.h" #include // // 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 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 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); }