#include "precomp.h" DEBUG_FILEZONE(ZONE_T120_APP_ROSTER); /* * arost.cpp * * Copyright (c) 1995 by DataBeam Corporation, Lexington, KY * * Abstract: * This is the implementation file for the Application Roster Class. This * class maintains the application roster, builds roster update and * refresh PDUs and manages the capabilities list which is part of the * application roster. * * This class makes use of a number of Rogue Wave lists to maintain the * roster entries and the capabilities list. The lists are organized in * such a way that the heirarchy of the conference can be maintained. This * is important to perform the necessary operations required by the T.124 * specification. In general, there is a main "Roster_Record_List" that * maintains a list of "AppRosterRecords". The list is indexed by the * GCC user ID where each record in the list holds a list of application * records (or entities) at that node, a list of capabilities for each * "entity" and a list of sub-nodes (the GCC user IDs of all the nodes * below this one in the connection hierarchy). The Roster_Record_List * only holds entries for immediately connected nodes. * * SEE INTERFACE FILE FOR A MORE DETAILED ABSTRACT * * Private Instance Variables: * m_pAppRosterMgr * Pointer to the object that will receive all owner callbacks. * m_cbDataMemory * This is the number of bytes required to hold the data associated * with a roster update message. This is calculated on a lock. * m_fTopProvider * Flag indicating if the node where this roster lives is the top * provider. * m_fLocalRoster * Flag indicating if the roster data is associated with a local * roster (maintaining intermediate node data) or global roster ( * (maintaining roster data for the whole conference). * m_pSessionKey * Pointer to a session key object that holds the session key * associated with this roster. * m_nInstance * The current instance of the roster. This number will change * whenever the roster is updated. * m_fRosterHasChanged * Flag indicating if the roster has changed since the last reset. * m_fPeerEntitiesAdded * Flag indicating if any APE records have been added to the * application roster since the last reset. * m_fPeerEntitiesRemoved * Flag indicating if any APE records have been deleted from the * application roster since the last reset. * m_fCapabilitiesHaveChanged * Flag indicating if the capabilities has changed since the last * reset. * m_NodeRecordList2 * List which contains all the application roster's node records. * m_CollapsedCapListForAllNodes * List which contains all the application roster's collapsed * capabilities. * m_fMaintainPduBuffer * Flag indicating if it is necessary for this roster object to * maintain internal PDU data. Won't be necessary for global rosters * at subordinate nodes. * m_fPduIsFlushed * Flag indicating if the PDU that currently exists has been flushed. * m_SetOfAppInfo * Pointer to internal PDU data. * m_pSetOfAppRecordUpdates * This instance variable keeps up with the current record update so * that it will not be necessary to search the entire list updates * each a new update is added to the internal PDU. * * Caveats: * None. * * Author: * blp */ #include "arost.h" #include "arostmgr.h" #include "clists.h" /* ** The maximum length the application data for a non-collapsed capablity ** can be. */ #define MAXIMUM_APPLICATION_DATA_LENGTH 255 /* * AppRosterRecord () * * Public Function Description * Constructor definition to instantiate the hash list dictionaries that * are used in an AppRosterRecord. This constructor is needed to allow * the AppRosterRecord structure to be directly instantiated with hash * list. */ APP_NODE_RECORD::APP_NODE_RECORD(void) : AppRecordList(DESIRED_MAX_APP_RECORDS), ListOfAppCapItemList2(DESIRED_MAX_CAP_LISTS), SubNodeList2(DESIRED_MAX_NODES) {} /* * CAppRoster () * * Public Function Description * When pGccSessKey is not NULL * This constructor is used to create an empty application roster. Note * that the session key for the roster must be passed in to the * constructor. * * When pSessKey is not NULL * This constructor builds a roster based on an indication pdu. * Application Roster objects may exist at nodes which do not have * applications to perform the necessary operations required by T.124 */ CAppRoster::CAppRoster ( PGCCSessionKey pGccSessKey,// create an empty app roster PSessionKey pPduSessKey,// build an app roster based on an indication pdu CAppRosterMgr *pAppRosterMgr, BOOL fTopProvider, BOOL fLocalRoster, BOOL fMaintainPduBuffer, PGCCError pRetCode) : CRefCount(MAKE_STAMP_ID('A','R','s','t')), m_nInstance(0), m_pAppRosterMgr(pAppRosterMgr), m_cbDataMemory(0), m_fTopProvider(fTopProvider), m_fLocalRoster(fLocalRoster), m_pSessionKey(NULL), m_fRosterHasChanged(FALSE), m_fPeerEntitiesAdded(FALSE), m_fPeerEntitiesRemoved(FALSE), m_fCapabilitiesHaveChanged(FALSE), m_NodeRecordList2(DESIRED_MAX_NODES), m_fMaintainPduBuffer(fMaintainPduBuffer), m_fPduIsFlushed(FALSE), m_pSetOfAppRecordUpdates(NULL) { DebugEntry(CAppRoster::CAppRoster); GCCError rc = GCC_NO_ERROR; ZeroMemory(&m_SetOfAppInfo, sizeof(m_SetOfAppInfo)); /* ** Here we store the session key of the roster. */ if (NULL != pGccSessKey) { ASSERT(NULL == pPduSessKey); DBG_SAVE_FILE_LINE m_pSessionKey = new CSessKeyContainer(pGccSessKey, &rc); } else if (NULL != pPduSessKey) { DBG_SAVE_FILE_LINE m_pSessionKey = new CSessKeyContainer(pPduSessKey, &rc); } else { ERROR_OUT(("CAppRoster::CAppRoster: invalid session key")); rc = GCC_BAD_SESSION_KEY; goto MyExit; } if (NULL == m_pSessionKey || GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::CAppRoster: can't create session key")); rc = GCC_ALLOCATION_FAILURE; // we do the cleanup in the destructor goto MyExit; } // Initialize the PDU structure to be no change. m_SetOfAppInfo.value.application_record_list.choice = APPLICATION_NO_CHANGE_CHOSEN; m_SetOfAppInfo.value.application_capabilities_list.choice = CAPABILITY_NO_CHANGE_CHOSEN; /* ** Here we go ahead and set up the session key portion of the ** PDU so we don't have to worry about it later. */ if (m_fMaintainPduBuffer) { rc = m_pSessionKey->GetSessionKeyDataPDU(&m_SetOfAppInfo.value.session_key); } ASSERT(GCC_NO_ERROR == rc); MyExit: DebugExitINT(CAppRoster:;CAppRoster, rc); *pRetCode = rc; } /* * ~CAppRoster () * * Public Function Description: * The destructor for the CAppRoster class is used to clean up * any memory allocated during the life of the object. */ CAppRoster::~CAppRoster(void) { /* * Free up all memory associated with the roster record list. */ ClearNodeRecordList(); // Clear the Collapsed Capabilities List. m_CollapsedCapListForAllNodes.DeleteList(); /* * Free up any outstanding PDU data. */ if (m_fMaintainPduBuffer) { FreeRosterUpdateIndicationPDU(); } /* * Free any memory associated with the session key.. */ if (NULL != m_pSessionKey) { m_pSessionKey->Release(); } } /* * Utilities that operate on roster update PDU strucutures. */ /* * GCCError FlushRosterUpdateIndicationPDU () * * Public Function Description * This routine is used to access any PDU data that might currently be * queued inside the application roster. PDU data is queued whenever * a request is made to the application roster that affects its * internal information base. */ void CAppRoster::FlushRosterUpdateIndicationPDU(PSetOfApplicationInformation *pSetOfAppInfo) { DebugEntry(CAppRoster::FlushRosterUpdateIndicationPDU); /* ** If this roster has already been flushed we will NOT allow the same ** PDU to be flushed again. Instead we delete the previously flushed ** PDU and set the flag back to unflushed. If another flush comes in ** before a PDU is built NULL will be returned in the application ** information pointer. */ if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU(); m_fPduIsFlushed = FALSE; } if ((m_SetOfAppInfo.value.application_record_list.choice != APPLICATION_NO_CHANGE_CHOSEN) || (m_SetOfAppInfo.value.application_capabilities_list.choice != CAPABILITY_NO_CHANGE_CHOSEN)) { if (m_SetOfAppInfo.value.application_record_list.choice == APPLICATION_NO_CHANGE_CHOSEN) { TRACE_OUT(("CAppRoster::FlushRosterUpdateIndicationPDU:" "Sending APPLICATION_NO_CHANGE_CHOSEN PDU")); } /* ** This section of the code sets up all the variables that don't ** pertain to the record list or the caps list. Note that the ** session key PDU data was set up in the constructor. Also note that ** the record list data and capabilities list data should be set up ** before this routine is called if there is any PDU traffic to issue. */ m_SetOfAppInfo.next = NULL; m_SetOfAppInfo.value.roster_instance_number = (USHORT) m_nInstance; m_SetOfAppInfo.value.peer_entities_are_added = (ASN1bool_t)m_fPeerEntitiesAdded; m_SetOfAppInfo.value.peer_entities_are_removed = (ASN1bool_t)m_fPeerEntitiesRemoved; /* ** Here we set up the pointer to the whole PDU structure associated ** with this application roster. */ *pSetOfAppInfo = &m_SetOfAppInfo; /* ** Setting this to true will cause the PDU data to be freed up the ** next time the roster object is entered insuring that new PDU ** data will be created. */ m_fPduIsFlushed = TRUE; } else { *pSetOfAppInfo = NULL; } } /* * GCCError BuildFullRefreshPDU () * * Public Function Description * This routine is responsible for generating a full application roster * refresh PDU. */ GCCError CAppRoster::BuildFullRefreshPDU(void) { GCCError rc; DebugEntry(CAppRoster::BuildFullRefreshPDU); /* ** Free up the old PDU data here if it is being maintained and the ** PDU has been flushed. Note that we also set the PDU is flushed boolean ** back to FALSE so that the new PDU will be maintained until it is ** flushed. */ if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU (); m_fPduIsFlushed = FALSE; } rc = BuildApplicationRecordListPDU (APP_FULL_REFRESH, 0, 0); if (rc == GCC_NO_ERROR) { BuildSetOfCapabilityRefreshesPDU (); } return rc; } /* * GCCError BuildApplicationRecordListPDU () * * Private Function Description * This routine creates an application roster update indication * PDU based on the passed in parameters. Memory used after this * routine is called is still owned by this object and will be * freed the next time this objects internal information base is * modified. * * Formal Parameters: * update_type - What type of update are we building. * user_id - node id of record to update. * entity_id - entity id of record to update. * * Return Value * GCC_NO_ERROR - No error occured. * GCC_INVALID_PARAMETER - Parameter passed in is invalid. * GCC_ALLOCATION_FAILURE - A resource error occured. * * Side Effects * None. * * Caveats * None. */ GCCError CAppRoster::BuildApplicationRecordListPDU ( APP_ROSTER_UPDATE_TYPE update_type, UserID user_id, EntityID entity_id) { GCCError rc = GCC_NO_ERROR; DebugEntry(CAppRoster::BuildApplicationRecordListPDU); if (m_fMaintainPduBuffer) { /* ** Note here that the top provider node always sends a full refresh ** PDU so there is no need to pay any attention to update type in ** this case. */ if ((update_type == APP_FULL_REFRESH) || m_fTopProvider) { /* ** First check to see if a refresh was already processed since the ** last PDU was flushed. If so we must free up the last refresh in ** preperation for the new one built here. Otherwise, if we have ** already started building an update this is not currently ** supported and is considered an error here. */ if (m_SetOfAppInfo.value.application_record_list.choice == APPLICATION_RECORD_REFRESH_CHOSEN) { FreeSetOfRefreshesPDU(); } else if (m_SetOfAppInfo.value.application_record_list.choice == APPLICATION_RECORD_UPDATE_CHOSEN) { ERROR_OUT(("CAppRoster::BuildApplicationRecordListPDU:" "ASSERTION: building refresh when update exists")); return GCC_INVALID_PARAMETER; } // This routine fills in the complete record list at this node. rc = BuildSetOfRefreshesPDU(); if (rc == GCC_NO_ERROR) { m_SetOfAppInfo.value.application_record_list.choice = APPLICATION_RECORD_REFRESH_CHOSEN; } } else if (update_type != APP_NO_CHANGE) { /* ** Here if there has already been a refresh PDU built we flag this ** as an error since we do not support both types of application ** information at the same time. */ if (m_SetOfAppInfo.value.application_record_list.choice == APPLICATION_RECORD_REFRESH_CHOSEN) { ERROR_OUT(("CAppRoster::BuildApplicationRecordListPDU:" "ASSERTION: building update when refresh exists")); return GCC_INVALID_PARAMETER; } // This routine fills in the specified update. rc = BuildSetOfUpdatesPDU(update_type, user_id, entity_id); if (rc == GCC_NO_ERROR) { /* ** If the first set of updates has not been used yet we ** initialize it here with the first update. */ if (m_SetOfAppInfo.value.application_record_list.choice == APPLICATION_NO_CHANGE_CHOSEN) { ASSERT(NULL != m_pSetOfAppRecordUpdates); m_SetOfAppInfo.value.application_record_list.u.application_record_update = m_pSetOfAppRecordUpdates; m_SetOfAppInfo.value.application_record_list.choice = APPLICATION_RECORD_UPDATE_CHOSEN; } } } } return rc; } /* * GCCError BuildSetOfRefreshesPDU () * * Private Function Description * This member function fills in the PDU with the entire set of roster * entries at this node. This is typically called when the Top Provider is * broadcasting a full refresh of the application roster. * * Formal Parameters * none * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - On resource failure * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::BuildSetOfRefreshesPDU(void) { GCCError rc = GCC_ALLOCATION_FAILURE; PSetOfApplicationRecordRefreshes pNewAppRecordRefreshes; PSetOfApplicationRecordRefreshes pOldAppRecordRefreshes = NULL; APP_NODE_RECORD *lpAppNodeRecord; APP_RECORD *lpAppRecData; CAppRecordList2 *lpAppRecDataList; UserID uid, uid2; EntityID eid; DebugEntry(CAppRoster::BuildSetOfRefreshesPDU); m_SetOfAppInfo.value.application_record_list.u.application_record_refresh = NULL; m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRecord = m_NodeRecordList2.Iterate(&uid))) { /* ** First we iterate through this nodes application record list. This ** encodes all the records local to this node. After this, all the ** sub nodes within this roster record will be encoded. */ lpAppNodeRecord->AppRecordList.Reset(); while (NULL != (lpAppRecData = lpAppNodeRecord->AppRecordList.Iterate(&eid))) { DBG_SAVE_FILE_LINE pNewAppRecordRefreshes = new SetOfApplicationRecordRefreshes; if (NULL == pNewAppRecordRefreshes) { goto MyExit; } if (m_SetOfAppInfo.value.application_record_list.u.application_record_refresh == NULL) { m_SetOfAppInfo.value.application_record_list.u.application_record_refresh = pNewAppRecordRefreshes; } else { pOldAppRecordRefreshes->next = pNewAppRecordRefreshes; } (pOldAppRecordRefreshes = pNewAppRecordRefreshes)->next = NULL; pNewAppRecordRefreshes->value.node_id = uid; pNewAppRecordRefreshes->value.entity_id = eid; // Fill in the application record. rc = BuildApplicationRecordPDU(lpAppRecData, &pNewAppRecordRefreshes->value.application_record); if (GCC_NO_ERROR != rc) { goto MyExit; } } // This section of the code copies the sub node records. lpAppNodeRecord->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRecord->SubNodeList2.Iterate(&uid2))) { lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate(&eid))) { DBG_SAVE_FILE_LINE pNewAppRecordRefreshes = new SetOfApplicationRecordRefreshes; if (NULL == pNewAppRecordRefreshes) { goto MyExit; } /* ** We must again check for null because it is possible ** to have an application roster with sub node records ** but no application records. */ if (m_SetOfAppInfo.value.application_record_list.u.application_record_refresh == NULL) { m_SetOfAppInfo.value.application_record_list.u.application_record_refresh = pNewAppRecordRefreshes; } else { pOldAppRecordRefreshes->next = pNewAppRecordRefreshes; } (pOldAppRecordRefreshes = pNewAppRecordRefreshes)->next = NULL; pNewAppRecordRefreshes->value.node_id = uid2; pNewAppRecordRefreshes->value.entity_id = eid; // Fill in the application record. rc = BuildApplicationRecordPDU (lpAppRecData, &pNewAppRecordRefreshes->value.application_record); if (GCC_NO_ERROR != rc) { goto MyExit; } } } } rc = GCC_NO_ERROR; MyExit: return rc; } /* * GCCError BuildSetOfUpdatesPDU () * * Private Function Description * This routine builds a single update based on the update type specified * in the passed in parameter. * * Formal Parameters * update_type - (i) Either APP_REPLACE_RECORD, APP_DELETE_RECORD, or * APP_ADD_RECORD. * node_id - (i) The node id of the update PDU record to build. * entity_id (i) The entity id of the update PDU record to build. * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - On resource failure * GCC_NO_SUCH_APPLICATION - If the specified record doesn't exist * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::BuildSetOfUpdatesPDU( APP_ROSTER_UPDATE_TYPE update_type, UserID node_id, EntityID entity_id) { GCCError rc = GCC_NO_ERROR; CAppRecordList2 *pAppRecordList; APP_RECORD *pAppRecord = NULL; APP_NODE_RECORD *node_record; DebugEntry(CAppRoster::BuildSetOfUpdatesPDU); /* ** We must first determine the pointer to the application record ** specified by the passed in user id and entity_id. We only do ** this search if the update type is not APP_DELETE_RECORD. */ if (update_type != APP_DELETE_RECORD) { if (NULL != (node_record = m_NodeRecordList2.Find(node_id))) { // Get a pointer to the application record from the entity id. pAppRecord = node_record->AppRecordList.Find(entity_id); } else { // Here we iterate through the sub-node list looking for the record m_NodeRecordList2.Reset(); while(NULL != (node_record = m_NodeRecordList2.Iterate())) { if (NULL != (pAppRecordList = node_record->SubNodeList2.Find(node_id))) { pAppRecord = pAppRecordList->Find(entity_id); break; } } } } /* ** Now if the application record was found or the update type is delete ** record we go ahead and encode the PDU here. */ if ((pAppRecord != NULL) || (update_type == APP_DELETE_RECORD)) { /* ** Here the record update will be NULL if it is the first record ** update being encoded. Otherwise we must bump the record to the ** next set of updates. */ DBG_SAVE_FILE_LINE PSetOfApplicationRecordUpdates pUpdates = new SetOfApplicationRecordUpdates; if (NULL == pUpdates) { return GCC_ALLOCATION_FAILURE; } pUpdates->next = NULL; if (m_pSetOfAppRecordUpdates == NULL) { m_pSetOfAppRecordUpdates = pUpdates; } else { // // LONCHANC: right now, append the new one. // but, can we prepend the new one??? // PSetOfApplicationRecordUpdates p; for (p = m_pSetOfAppRecordUpdates; NULL != p->next; p = p->next) ; p->next = pUpdates; } /* * This routine only returns one record. */ pUpdates->value.node_id = node_id; pUpdates->value.entity_id = entity_id; switch (update_type) { case APP_ADD_RECORD: pUpdates->value.application_update.choice = APPLICATION_ADD_RECORD_CHOSEN; BuildApplicationRecordPDU(pAppRecord, &(pUpdates->value.application_update.u.application_add_record)); break; case APP_REPLACE_RECORD: pUpdates->value.application_update.choice = APPLICATION_REPLACE_RECORD_CHOSEN; rc = BuildApplicationRecordPDU(pAppRecord, &(pUpdates->value.application_update.u.application_replace_record)); break; default: /* * The record does not have to be filled in for this case. */ pUpdates->value.application_update.choice = APPLICATION_REMOVE_RECORD_CHOSEN; break; } } else { WARNING_OUT(("CAppRoster::BuildSetOfUpdatesPDU: Assertion:" "No applicaton record found for PDU")); rc = GCC_NO_SUCH_APPLICATION; } return rc; } /* * GCCError BuildApplicationRecordPDU () * * Private Function Description * This routine build a single application record for a PDU. A pointer to * the record is passed in to the routine. * * Formal Parameters * application_record - (i) Record to be encoded. * application_record_pdu - (i) PDU to fill in. * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - A resource error occured. * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::BuildApplicationRecordPDU( APP_RECORD *pAppRecord, PApplicationRecord pAppRecordPdu) { GCCError rc = GCC_NO_ERROR; DebugEntry(CAppRoster::BuildApplicationRecordPDU); pAppRecordPdu->bit_mask = 0; if (! pAppRecord->non_collapsed_caps_list.IsEmpty()) { pAppRecordPdu->bit_mask |= NON_COLLAPSING_CAPABILITIES_PRESENT; rc = BuildSetOfNonCollapsingCapabilitiesPDU( &pAppRecordPdu->non_collapsing_capabilities, &pAppRecord->non_collapsed_caps_list); if (GCC_NO_ERROR != rc) { goto MyExit; } } // Fill in the startup channel type if it is specified if (pAppRecord->startup_channel_type != MCS_NO_CHANNEL_TYPE_SPECIFIED) { pAppRecordPdu->bit_mask |= RECORD_STARTUP_CHANNEL_PRESENT; pAppRecordPdu->record_startup_channel = (ChannelType) pAppRecord->startup_channel_type; } // Fill in the application user id if one is specified if (pAppRecord->application_user_id != 0) { pAppRecordPdu->bit_mask |= APPLICATION_USER_ID_PRESENT; pAppRecordPdu->application_user_id = pAppRecord->application_user_id; } // Fill in the required fields pAppRecordPdu->application_is_active = (ASN1bool_t)pAppRecord->is_enrolled_actively; pAppRecordPdu->is_conducting_capable = (ASN1bool_t)pAppRecord->is_conducting_capable; ASSERT(GCC_NO_ERROR == rc); MyExit: return rc; } /* * GCCError BuildSetOfCapabilityRefreshesPDU () * * Private Function Description * This routine builds a PDU structure with the complete set of * capabilities maintained at this node. * * Formal Parameters * None * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATIONFAILURE - On resource failure * * Side Effects * None * * Caveats * The standard allows us to send a zero length set of capabilities when * an application leaves that previously had capabilites. */ GCCError CAppRoster::BuildSetOfCapabilityRefreshesPDU(void) { GCCError rc = GCC_ALLOCATION_FAILURE; PSetOfApplicationCapabilityRefreshes pNew; PSetOfApplicationCapabilityRefreshes pOld = NULL; DebugEntry(CAppRoster::BuildSetOfCapabilityRefreshesPDU); if (m_fMaintainPduBuffer) { APP_CAP_ITEM *lpAppCapData; /* ** We must first free up any previously built PDU data associated ** with a capability refresh. */ if (m_SetOfAppInfo.value.application_capabilities_list.choice == APPLICATION_CAPABILITY_REFRESH_CHOSEN) { FreeSetOfCapabilityRefreshesPDU (); } m_SetOfAppInfo.value.application_capabilities_list.choice = APPLICATION_CAPABILITY_REFRESH_CHOSEN; m_SetOfAppInfo.value.application_capabilities_list.u.application_capability_refresh = NULL; // Iterate through the complete list of capabilities. m_CollapsedCapListForAllNodes.Reset(); while (NULL != (lpAppCapData = m_CollapsedCapListForAllNodes.Iterate())) { DBG_SAVE_FILE_LINE pNew = new SetOfApplicationCapabilityRefreshes; if (NULL == pNew) { goto MyExit; } /* ** If the set of capability refreshes pointer is equal to NULL ** we are at the first capability. Here we need to save the ** pointer to the first capability. */ if (m_SetOfAppInfo.value.application_capabilities_list.u. application_capability_refresh == NULL) { m_SetOfAppInfo.value.application_capabilities_list.u. application_capability_refresh = pNew; } else { pOld->next = pNew; } /* ** This is used to set the next pointer if another record ** exists after this one. */ /* * This will get filled in later if there is another record. */ (pOld = pNew)->next = NULL; // Fill in the capability identifier rc = lpAppCapData->pCapID->GetCapabilityIdentifierDataPDU( &pNew->value.capability_id); if (GCC_NO_ERROR != rc) { goto MyExit; } // Fill in the capability choice from the GCC capability class. pNew->value.capability_class.choice = (USHORT) lpAppCapData->eCapType; // Note that nothing is filled in for a logical capability. if (lpAppCapData->eCapType == GCC_UNSIGNED_MINIMUM_CAPABILITY) { pNew->value.capability_class.u.unsigned_minimum = lpAppCapData->nUnsignedMinimum; } else if (lpAppCapData->eCapType == GCC_UNSIGNED_MAXIMUM_CAPABILITY) { pNew->value.capability_class.u.unsigned_maximum = lpAppCapData->nUnsignedMaximum; } // Fill in number of entities regardless of capability type. pNew->value.number_of_entities = lpAppCapData->cEntries; } } rc = GCC_NO_ERROR; MyExit: return rc; } /* * ApplicationRosterError BuildSetOfNonCollapsingCapabilitiesPDU () * * Private Function Description * This routine builds a PDU structure for the non collapsing capabilities * list associated passed in. * * Formal Parameters * pSetOfCaps - (o) PDU structure to fill in * capabilities_list - (i) Source non-collapsing capabilities. * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATIONFAILURE - On resource failure * * Side Effects * None * * Caveats * None */ GCCError CAppRoster::BuildSetOfNonCollapsingCapabilitiesPDU( PSetOfNonCollapsingCapabilities *pSetOfCaps, CAppCapItemList *pAppCapItemList) { GCCError rc = GCC_ALLOCATION_FAILURE; PSetOfNonCollapsingCapabilities new_set_of_capabilities; PSetOfNonCollapsingCapabilities old_set_of_capabilities; APP_CAP_ITEM *lpAppCapData; DebugEntry(CAppRoster::BuildSetOfNonCollapsingCapabilitiesPDU); *pSetOfCaps = NULL; old_set_of_capabilities = NULL; // Setting this to NULL removes warning /* * Iterate through the complete list of capabilities. */ pAppCapItemList->Reset(); while (NULL != (lpAppCapData = pAppCapItemList->Iterate())) { DBG_SAVE_FILE_LINE new_set_of_capabilities = new SetOfNonCollapsingCapabilities; if (NULL == new_set_of_capabilities) { goto MyExit; } /* ** If the passed in pointer is equal to NULL we are at the first ** capability. Here we need to save the pointer to the first ** capability in the passed in pointer. */ if (*pSetOfCaps == NULL) { *pSetOfCaps = new_set_of_capabilities; } else { if(old_set_of_capabilities != NULL) { old_set_of_capabilities->next = new_set_of_capabilities; } } /* ** This is used to set the next pointer if another record exists ** after this one. */ old_set_of_capabilities = new_set_of_capabilities; /* * This will get filled in later if there is another record. */ new_set_of_capabilities->next = NULL; new_set_of_capabilities->value.bit_mask = 0; // Fill in the capability identifier rc = lpAppCapData->pCapID->GetCapabilityIdentifierDataPDU( &new_set_of_capabilities->value.capability_id); if (GCC_NO_ERROR != rc) { goto MyExit; } if ((lpAppCapData->poszAppData != NULL) && (rc == GCC_NO_ERROR)) { new_set_of_capabilities->value.bit_mask |= APPLICATION_DATA_PRESENT; new_set_of_capabilities->value.application_data.length = lpAppCapData->poszAppData->length; new_set_of_capabilities->value.application_data.value = lpAppCapData->poszAppData->value; } } rc = GCC_NO_ERROR; MyExit: if(rc != GCC_NO_ERROR) { FreeSetOfNonCollapsingCapabilitiesPDU(*pSetOfCaps); *pSetOfCaps = NULL; } return rc; } /* * These routines are used to free up a roster update indication PDU. */ /* * void FreeRosterUpdateIndicationPDU () * * Private Function Description * This routine frees up all the internal data allocated to hold the roster * update PDU. * * Formal Parameters * None * * Return Value * None * * Side Effects * None * * Caveats * Note that the session key PDU data is not freed. Since this data will * not change through out the life of this application roster object * we just use the same session id PDU data for every roster update * indication. */ void CAppRoster::FreeRosterUpdateIndicationPDU(void) { DebugEntry(CAppRoster::FreeRosterUpdateIndicationPDU); switch (m_SetOfAppInfo.value.application_record_list.choice) { case APPLICATION_RECORD_REFRESH_CHOSEN: FreeSetOfRefreshesPDU (); break; case APPLICATION_RECORD_UPDATE_CHOSEN: FreeSetOfUpdatesPDU (); break; } // Free the PDU data associated with the capability list if one exists. if (m_SetOfAppInfo.value.application_capabilities_list.choice == APPLICATION_CAPABILITY_REFRESH_CHOSEN) { FreeSetOfCapabilityRefreshesPDU (); } m_SetOfAppInfo.value.application_record_list.choice = APPLICATION_NO_CHANGE_CHOSEN; m_SetOfAppInfo.value.application_capabilities_list.choice = CAPABILITY_NO_CHANGE_CHOSEN; m_pSetOfAppRecordUpdates = NULL; } /* * void FreeSetOfRefreshesPDU () * * Private Function Description * This routine Frees all the memory associated with a set * of application record refreshes. * * Formal Parameters * none * * Return Value * none * * Side Effects * none * * Caveats * none */ void CAppRoster::FreeSetOfRefreshesPDU(void) { PSetOfApplicationRecordRefreshes pCurr, pNext; DebugEntry(CAppRoster::FreeSetOfRefreshesPDU); for (pCurr = m_SetOfAppInfo.value.application_record_list.u.application_record_refresh; NULL != pCurr; pCurr = pNext) { pNext = pCurr->next; // Free up any non-collapsing capabilities data if (pCurr->value.application_record.bit_mask & NON_COLLAPSING_CAPABILITIES_PRESENT) { FreeSetOfNonCollapsingCapabilitiesPDU(pCurr->value.application_record.non_collapsing_capabilities); } // Delete the actual record refresh delete pCurr; } m_SetOfAppInfo.value.application_record_list.u.application_record_refresh = NULL; } /* * void FreeSetOfUpdatesPDU () * * Private Function Description * This routine frees the memory associated with a complete set * application roster updates. * * Formal Parameters * none * * Return Value * none * * Side Effects * none * * Caveats * none */ void CAppRoster::FreeSetOfUpdatesPDU(void) { PSetOfApplicationRecordUpdates pCurr, pNext; PApplicationRecord application_record; DebugEntry(CAppRoster::FreeSetOfUpdatesPDU); for (pCurr = m_SetOfAppInfo.value.application_record_list.u.application_record_update; NULL != pCurr; pCurr = pNext) { // remember the next one because we will free the current one pNext = pCurr->next; // Free up any non-collapsing capabilities data switch(pCurr->value.application_update.choice) { case APPLICATION_ADD_RECORD_CHOSEN: application_record = &pCurr->value.application_update.u.application_add_record; break; case APPLICATION_REPLACE_RECORD_CHOSEN: application_record = &pCurr->value.application_update.u.application_replace_record; break; default: application_record = NULL; break; } if (application_record != NULL) { if (application_record->bit_mask & NON_COLLAPSING_CAPABILITIES_PRESENT) { FreeSetOfNonCollapsingCapabilitiesPDU(application_record->non_collapsing_capabilities); } } // Delete the actual update structure delete pCurr; } m_SetOfAppInfo.value.application_record_list.u.application_record_update = NULL; } /* * void FreeSetOfCapabilityRefreshesPDU () * * Private Function Description * This routine frees all the memory associated with the capability PDU. * * Formal Parameters * capability_refresh - (i) Capabilities to be freed. * * Return Value * none * * Side Effects * none * * Caveats * Note that the capability id PDU data is not freed here. Since this * data should not change through out the life of this object we don't * bother freeing and regenerating it. */ void CAppRoster::FreeSetOfCapabilityRefreshesPDU(void) { PSetOfApplicationCapabilityRefreshes pCurr, pNext; for (pCurr = m_SetOfAppInfo.value.application_capabilities_list.u.application_capability_refresh; NULL != pCurr; pCurr = pNext) { pNext = pCurr->next; delete pCurr; } } /* * void FreeSetOfNonCollapsingCapabilitiesPDU () * * Private Function Description * This routine frees all the memory associated with the * non-collapsed capability PDU. * * Formal Parameters * capability_refresh - (i) Non-Collapsed Capabilities to be freed. * * Return Value * none * * Side Effects * none * * Caveats * Note that the capability id PDU data is not freed here. Since this * data should not change through out the life of this object we don't * bother freeing and regenerating it. */ void CAppRoster::FreeSetOfNonCollapsingCapabilitiesPDU ( PSetOfNonCollapsingCapabilities capability_refresh) { PSetOfNonCollapsingCapabilities pCurr, pNext; for (pCurr = capability_refresh; NULL != pCurr; pCurr = pNext) { pNext = pCurr->next; delete pCurr; } } /* * These routines process roster update indication PDUs. */ /* * ApplicationRosterError ProcessRosterUpdateIndicationPDU () * * Public Function Description * This routine is responsible for processing the decoded PDU data. * It essentially changes the application roster object's internal database * based on the information in the structure. */ GCCError CAppRoster::ProcessRosterUpdateIndicationPDU ( PSetOfApplicationInformation application_information, UserID sender_id) { GCCError rc = GCC_NO_ERROR; DebugEntry(CAppRoster::ProcessRosterUpdateIndicationPDU); /* ** Free up the old PDU data here if it is being maintained and the ** PDU has been flushed. Note that we also set the PDU is flushed boolean ** back to FALSE so that the new PDU will be maintained until it is ** flushed. */ if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU (); m_fPduIsFlushed = FALSE; } /* ** Now check the application key to make sure we have a match. If ** not, return with no change. */ if (! m_pSessionKey->IsThisYourSessionKeyPDU(&application_information->value.session_key)) { WARNING_OUT(("CAppRoster::ProcessRosterUpdateIndicationPDU:GCC_BAD_SESSION_KEY")); rc = GCC_BAD_SESSION_KEY; goto MyExit; } /* ** If this is a roster update and refresh is chosen we must ** clear out the entire list and rebuild it. */ if (application_information->value.application_record_list.choice != APPLICATION_NO_CHANGE_CHOSEN) { // The roster is about to change m_fRosterHasChanged = TRUE; /* ** If this node is the top provider or this roster is local and ** only used to propogate PDUs up toward the top provider, ** we increment the instance number. If it is not we get the ** instance number out of the PDU. */ if (m_fTopProvider || m_fLocalRoster) { m_nInstance++; } else { m_nInstance = application_information->value.roster_instance_number; } /* ** Here if either of these booleans is already TRUE we do not ** want to write over them with this PDU data. Therefore, we ** check for FALSE before we do anything with them. */ if (! m_fPeerEntitiesAdded) { m_fPeerEntitiesAdded = application_information->value.peer_entities_are_added; } if (! m_fPeerEntitiesRemoved) { m_fPeerEntitiesRemoved = application_information->value.peer_entities_are_removed; } if (application_information->value.application_record_list.choice == APPLICATION_RECORD_REFRESH_CHOSEN) { TRACE_OUT(("CAppRoster::ProcessRosterUpdateIndicationPDU:ProcessSetOfRefreshesPDU")); rc = ProcessSetOfRefreshesPDU( application_information->value.application_record_list.u.application_record_refresh, sender_id); } else { TRACE_OUT(("CAppRoster::ProcessRosterUpdateIndicationPDU:ProcessSetOfUpdatesPDU")); rc = ProcessSetOfUpdatesPDU( application_information->value.application_record_list.u.application_record_update, sender_id); } if (GCC_NO_ERROR != rc) { goto MyExit; } } else { ERROR_OUT(("AppRoster::ProcessRosterUpdateIndicationPDU:ASSERTION: NO Change PDU received")); } // Process the capabilities list portion of the PDU. if (application_information->value.application_capabilities_list.choice == APPLICATION_CAPABILITY_REFRESH_CHOSEN) { // Set flag to show that change has occured. m_fCapabilitiesHaveChanged = TRUE; /* ** We will store the new capabilities in the roster record ** associated with the sender id. Note that it is possible for ** this roster record to contain an empty application record list ** if the sending node has no enrolled applications. */ rc = ProcessSetOfCapabilityRefreshesPDU( application_information->value.application_capabilities_list.u.application_capability_refresh, sender_id); } else { ASSERT(GCC_NO_ERROR == rc); } MyExit: return rc; } /* * GCCError ProcessSetOfRefreshesPDU () * * Private Function Description * This routine processes a set of record refreshes. It is responsible * for managing the creation (or update) of all affected application * records. The roster list built from a refresh PDU does not maintain the * hierarchy of the conference since it is not important at this point. * Refreshes are issued as broacast from the Top Provider down to the * sub-ordinate nodes. * * Formal Parameters * record_refresh - (i) Set of record refresh PDUs to be processed. * sender_id - (i) Node id of node that sent the update. * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - On resource failure * * Side Effects * none * * Caveate * none */ GCCError CAppRoster::ProcessSetOfRefreshesPDU( PSetOfApplicationRecordRefreshes record_refresh, UserID sender_id) { GCCError rc = GCC_ALLOCATION_FAILURE; PSetOfApplicationRecordRefreshes pCurr; APP_RECORD *app_record; APP_NODE_RECORD *node_record = NULL; CAppRecordList2 *record_list=NULL; UserID node_id; EntityID entity_id; DebugEntry(CAppRoster::ProcessSetOfRefreshesPDU); if (record_refresh != NULL) { // Clear out the node record for the sender id ClearNodeRecordFromList (sender_id); /* ** Create the node record for the sender id passed into this routine. ** Note that if the sender of this refresh is the Top Provider ** all nodes below the top provider are contained in the sub node ** list of the Top Provider's node record. */ DBG_SAVE_FILE_LINE node_record = new APP_NODE_RECORD; if (NULL == node_record) { goto MyExit; } if(!m_NodeRecordList2.Append(sender_id, node_record)) { goto MyExit; } for (pCurr = record_refresh; NULL != pCurr; pCurr = pCurr->next) { node_id = pCurr->value.node_id; entity_id = pCurr->value.entity_id; if (sender_id != node_id) { // Get or create the sub node record list if (NULL == (record_list = node_record->SubNodeList2.Find(node_id))) { DBG_SAVE_FILE_LINE record_list = new CAppRecordList2(DESIRED_MAX_APP_RECORDS); if (NULL == record_list) { goto MyExit; } node_record->SubNodeList2.Append(node_id, record_list); } } else { /* ** Here we set up the pointer to the record list. This ** list is the node records application list which ** means that this list contains the application records ** associated with the sender's node. */ record_list = &node_record->AppRecordList; } // Now create and fill in the new application record. DBG_SAVE_FILE_LINE app_record = new APP_RECORD; if (NULL == app_record) { goto MyExit; } rc = ProcessApplicationRecordPDU(app_record, &pCurr->value.application_record); if (GCC_NO_ERROR != rc) { goto MyExit; } record_list->Append(entity_id, app_record); } // for } else { // This roster no longer contains any entries so clear the list!!! ClearNodeRecordList (); } /* ** Build a full refresh PDU here if no errors occured while processing ** the refresh PDU. */ rc = BuildApplicationRecordListPDU (APP_FULL_REFRESH, 0, 0); MyExit: if(rc ==GCC_ALLOCATION_FAILURE) { if(node_record) { delete node_record; } } return rc; } /* * GCCError ProcessSetOfUpdatesPDU () * * Private Function Description * This routine processes a set of roster updates. It iterates through * the complete list of updates making all necessary changes to the * internal information base and building the appropriate PDU. * * Formal Parameters * record_update - (i) set of updates PDU to be processed * sender_id - (i) gcc user id of node that sent the update * * Return Value * APP_ROSTER_NO_ERROR - On Success * APP_ROSTER_RESOURCE_ERROR - On resource failure * * Side Effects * none * * Caveate * none */ GCCError CAppRoster::ProcessSetOfUpdatesPDU( PSetOfApplicationRecordUpdates record_update, UserID sender_id) { GCCError rc = GCC_ALLOCATION_FAILURE; PSetOfApplicationRecordUpdates pCurr; UserID node_id; EntityID entity_id; PApplicationRecord pdu_record; APP_RECORD *application_record = NULL; APP_NODE_RECORD *node_record; CAppRecordList2 *record_list; APP_ROSTER_UPDATE_TYPE update_type; DebugEntry(CAppRoster::ProcessSetOfUpdatesPDU); if (record_update != NULL) { for (pCurr = record_update; NULL != pCurr; pCurr = pCurr->next) { node_id = pCurr->value.node_id; entity_id = pCurr->value.entity_id; switch(pCurr->value.application_update.choice) { case APPLICATION_ADD_RECORD_CHOSEN: pdu_record = &(pCurr->value.application_update.u.application_add_record); update_type = APP_ADD_RECORD; break; case APPLICATION_REPLACE_RECORD_CHOSEN: DeleteRecord (node_id, entity_id, FALSE); pdu_record = &(pCurr->value.application_update.u.application_replace_record); update_type = APP_REPLACE_RECORD; break; default: // Remove record /* ** Inform the owner that a record was delete while processing ** this PDU so that it can perform any necessary cleanup. */ m_pAppRosterMgr->DeleteRosterRecord(node_id, entity_id); DeleteRecord (node_id, entity_id, TRUE); pdu_record = NULL; update_type = APP_DELETE_RECORD; break; } /* ** First get the roster record and if one does not exist for this ** app record create it. After that we will create the application ** record and put it into the correct slot in the application ** roster record. */ if (pdu_record != NULL) { /* ** First find the correct node record and if it does not ** exist create it. */ if (NULL == (node_record = m_NodeRecordList2.Find(sender_id))) { DBG_SAVE_FILE_LINE node_record = new APP_NODE_RECORD; if (NULL == node_record) { goto MyExit; } m_NodeRecordList2.Append(sender_id, node_record); } /* ** If the user and sender id is the same then the record ** will be contained in the app_record_list. Otherwise, it ** will be maintained in the sub-node list. */ /* ** If the sender_id equals the node id being processed ** use the application record list instead of the sub ** node list. */ if (sender_id != node_id) { /* ** Find the correct node list and create it if it does ** not exists. This list holds lists of all the ** application peer entities at a node. */ if (NULL == (record_list = node_record->SubNodeList2.Find(node_id))) { DBG_SAVE_FILE_LINE record_list = new CAppRecordList2(DESIRED_MAX_APP_RECORDS); if (NULL == record_list) { goto MyExit; } node_record->SubNodeList2.Append(node_id, record_list); } } else { record_list = &node_record->AppRecordList; } // Now fill in the application record DBG_SAVE_FILE_LINE application_record = new APP_RECORD; if (NULL == application_record) { goto MyExit; } record_list->Append(entity_id, application_record); rc = ProcessApplicationRecordPDU(application_record, pdu_record); if (GCC_NO_ERROR != rc) { goto MyExit; } } // if /* ** Here we add this update to our PDU and jump to the next update ** in the PDU currently being processed. */ rc = BuildApplicationRecordListPDU ( update_type, node_id, entity_id); if (rc != GCC_NO_ERROR) { goto MyExit; } /* ** If the capabilities changed during the above processing ** we must create a new collapsed capabilities list and ** build a new capability refresh PDU. */ if (m_fCapabilitiesHaveChanged) { MakeCollapsedCapabilitiesList(); rc = BuildSetOfCapabilityRefreshesPDU (); if (rc != GCC_NO_ERROR) { goto MyExit; } } } // for } // if rc = GCC_NO_ERROR; MyExit: return rc; } /* * GCCError ProcessApplicationRecordPDU () * * Private Function Description * This routine is responsible for decoding the Application Record * portion of the roster update pdu. * * Formal Parameters * application_record - This is the internal destination app record. * pdu_record - Source PDU data * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - On resource failure * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::ProcessApplicationRecordPDU ( APP_RECORD *application_record, PApplicationRecord pdu_record) { GCCError rc = GCC_NO_ERROR; DebugEntry(CAppRoster::ProcessApplicationRecordPDU); application_record->is_enrolled_actively = pdu_record->application_is_active; application_record->is_conducting_capable = pdu_record->is_conducting_capable; if (pdu_record->bit_mask & RECORD_STARTUP_CHANNEL_PRESENT) { application_record->startup_channel_type = (MCSChannelType)pdu_record->record_startup_channel; } else application_record->startup_channel_type= MCS_NO_CHANNEL_TYPE_SPECIFIED; if (pdu_record->bit_mask & APPLICATION_USER_ID_PRESENT) { application_record->application_user_id = pdu_record->application_user_id; } else application_record->application_user_id = 0; if (pdu_record->bit_mask & NON_COLLAPSING_CAPABILITIES_PRESENT) { rc = ProcessNonCollapsingCapabilitiesPDU ( &application_record->non_collapsed_caps_list, pdu_record->non_collapsing_capabilities); } return rc; } /* * GCCError ProcessSetOfCapabilityRefreshesPDU () * * Private Function Description * This routine is responsible for decoding the capabilities portion * of an roster update PDU. * * Formal Parameters * capability_refresh - (i) set of capabilities PDU to be processed * sender_id - (i) gcc user id of node that sent the update * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - On resource failure * * Side Effects * none * * Caveats * This routine does handle NULL for the capability refresh which means * that the capabilities delivered no longer exists. */ GCCError CAppRoster::ProcessSetOfCapabilityRefreshesPDU( PSetOfApplicationCapabilityRefreshes capability_refresh, UserID sender_id) { GCCError rc = GCC_NO_ERROR; PSetOfApplicationCapabilityRefreshes pCurr; CAppCapItemList *pAppCapList; APP_CAP_ITEM *pAppCapItem; APP_NODE_RECORD *node_record; DebugEntry(CAppRoster::ProcessSetOfCapabilityRefreshesPDU); if (NULL == (node_record = m_NodeRecordList2.Find(sender_id))) { DBG_SAVE_FILE_LINE node_record = new APP_NODE_RECORD; if (NULL == node_record) { return GCC_ALLOCATION_FAILURE; } if(!m_NodeRecordList2.Append(sender_id, node_record)) { delete node_record; return GCC_ALLOCATION_FAILURE; } } // get collapsed cap list ptr pAppCapList = &node_record->CollapsedCapList; // Clear out all the old capabilities from this list. pAppCapList->DeleteList(); // Begin processing the PDU. for (pCurr = capability_refresh; NULL != pCurr; pCurr = pCurr->next) { ASSERT(GCC_NO_ERROR == rc); // Create and fill in the new capability. DBG_SAVE_FILE_LINE pAppCapItem = new APP_CAP_ITEM((GCCCapabilityType) pCurr->value.capability_class.choice); if (NULL == pAppCapItem) { return GCC_ALLOCATION_FAILURE; } // Create the capability ID DBG_SAVE_FILE_LINE pAppCapItem->pCapID = new CCapIDContainer(&pCurr->value.capability_id, &rc); if (NULL == pAppCapItem->pCapID) { rc = GCC_ALLOCATION_FAILURE; } if (GCC_NO_ERROR != rc) { delete pAppCapItem; return rc; } // append this cap to the collapsed cap list pAppCapList->Append(pAppCapItem); /* ** Note that a logical type's value is maintained as ** number of entities. */ if (pCurr->value.capability_class.choice == UNSIGNED_MINIMUM_CHOSEN) { pAppCapItem->nUnsignedMinimum = pCurr->value.capability_class.u.unsigned_minimum; } else if (pCurr->value.capability_class.choice == UNSIGNED_MAXIMUM_CHOSEN) { pAppCapItem->nUnsignedMaximum = pCurr->value.capability_class.u.unsigned_maximum; } pAppCapItem->cEntries = pCurr->value.number_of_entities; } // for // This forces a new capabilities list to be calculated. MakeCollapsedCapabilitiesList(); /* ** Here we build the new PDU data associated with this refresh of the ** capability list. */ return BuildSetOfCapabilityRefreshesPDU(); } /* * GCCError ProcessNonCollapsingCapabilitiesPDU () * * Private Function Description * This routine is responsible for decoding the non-collapsing capabilities * portion of a roster record PDU. * * Formal Parameters * non_collapsed_caps_list - (o) Pointer to list to fill in with new * non-collapsed caps. * pSetOfCaps - (i) non-collapsed PDU data * * Return Value * GCC_NO_ERROR - On Success * GCC_ALLOCATION_FAILURE - On resource failure * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::ProcessNonCollapsingCapabilitiesPDU ( CAppCapItemList *non_collapsed_caps_list, PSetOfNonCollapsingCapabilities pSetOfCaps) { GCCError rc = GCC_NO_ERROR; PSetOfNonCollapsingCapabilities pCurr; APP_CAP_ITEM *pAppCapItem; DebugEntry(CAppRoster::ProcessNonCollapsingCapsPDU); for (pCurr = pSetOfCaps; NULL != pCurr; pCurr = pCurr->next) { // // LONCHANC: The following cap data does not have a type??? // for now, set it to zero. // DBG_SAVE_FILE_LINE pAppCapItem = new APP_CAP_ITEM((GCCCapabilityType)0); if (NULL == pAppCapItem) { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } DBG_SAVE_FILE_LINE pAppCapItem->pCapID = new CCapIDContainer(&pCurr->value.capability_id, &rc); if (NULL == pAppCapItem->pCapID) { rc = GCC_ALLOCATION_FAILURE; } if (rc != GCC_NO_ERROR) { goto MyExit; } if (pCurr->value.bit_mask & APPLICATION_DATA_PRESENT) { if (NULL == (pAppCapItem->poszAppData = ::My_strdupO2( pCurr->value.application_data.value, pCurr->value.application_data.length))) { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } if( !non_collapsed_caps_list->Append(pAppCapItem) ) { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } // for ASSERT(GCC_NO_ERROR == rc); MyExit: if (GCC_NO_ERROR != rc) { if( NULL != pAppCapItem ) delete pAppCapItem; } return rc; } /* * Utilities that operate on conference records. */ /* * UINT LockApplicationRoster () * * Public Function Description * This routine is used to lock a GCCApplicationRoster and to determine the * amount of memory necessary to hold the data referenced by the "API" * application roster structure. The GCCApplicationRoster is used in * indications to applications at the local node. */ UINT CAppRoster::LockApplicationRoster(void) { UINT number_of_records = 0; UINT number_of_capabilities = 0; APP_NODE_RECORD *lpAppNodeRecord; APP_RECORD *lpAppRecData; APP_CAP_ITEM *lpAppCapData; CAppRecordList2 *lpAppRecDataList; DebugEntry(CAppRoster::LockApplicationRoster); /* * If this is the first time this routine is called, determine the size of * the memory required to hold the data referenced by the application * roster structure. Otherwise, just increment the lock count. */ if (Lock() == 1) { /* * Lock the data for the session key held within the roster. This lock * call returns the size of the memory required to hold the session key * data, rounded to an even multiple of four-bytes. */ m_cbDataMemory = m_pSessionKey->LockSessionKeyData(); /* * First calculate the total number of records. This count is used to * determine the space necessary to hold the records. Note that we must * count both the application record list and the sub-node list. */ m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRecord = m_NodeRecordList2.Iterate())) { /* * Add the application records at this node to the count. */ number_of_records += lpAppNodeRecord->AppRecordList.GetCount(); /* * Next count the sub node records. */ if (! lpAppNodeRecord->SubNodeList2.IsEmpty()) { lpAppNodeRecord->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRecord->SubNodeList2.Iterate())) { number_of_records += lpAppRecDataList->GetCount(); } } } /* * Now determine the amount of memory necessary to hold all of the * pointers to the application records as well as the actual * GCCApplicationRecord structures. */ m_cbDataMemory += number_of_records * (sizeof(PGCCApplicationRecord) + ROUNDTOBOUNDARY( sizeof(GCCApplicationRecord)) ); m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRecord = m_NodeRecordList2.Iterate())) { /* * Iterate through this node's record list, determining the amount * of memory necessary to hold the pointers to the non-collapsing * capabilities as well as the capability ID data and octet string * data associated with each non-collapsing capability. */ lpAppNodeRecord->AppRecordList.Reset(); while (NULL != (lpAppRecData = lpAppNodeRecord->AppRecordList.Iterate())) { /* * Set up an iterator for the list of non-collapsing * capabilities held within each application roster. */ lpAppRecData->non_collapsed_caps_list.Reset(); number_of_capabilities += lpAppRecData->non_collapsed_caps_list.GetCount(); while (NULL != (lpAppCapData = lpAppRecData->non_collapsed_caps_list.Iterate())) { /* * Lock the data for each capability ID. The lock call * returns the length of the data referenced by each * capability ID rounded to occupy an even multiple of * four-bytes. */ m_cbDataMemory += lpAppCapData->pCapID->LockCapabilityIdentifierData(); /* * Add up the space required to hold the application data * octet strings if they are present. Make sure there is * enough space for each octet string to occupy an even * multiple of four bytes. Add room to hold the actual * octet string structure also since the capability * structure only contains a pointer to a OSTR. */ if (lpAppCapData->poszAppData != NULL) { m_cbDataMemory += ROUNDTOBOUNDARY(sizeof(OSTR)); m_cbDataMemory += ROUNDTOBOUNDARY(lpAppCapData->poszAppData->length); } } } /* * Iterate through this node's sub-node record list, determining the * amount of memory necessary to hold the pointers to the * non-collapsing capabilities as well as the capability ID data and * octet string data associated with each non-collapsing capability. */ lpAppNodeRecord->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRecord->SubNodeList2.Iterate())) { lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate())) { /* * Set up an iterator for the list of non-collapsing * capabilities held within each application roster. */ number_of_capabilities += lpAppRecData->non_collapsed_caps_list.GetCount(); lpAppRecData->non_collapsed_caps_list.Reset(); while (NULL != (lpAppCapData = lpAppRecData->non_collapsed_caps_list.Iterate())) { /* * Lock the data for each capability ID. The lock call * returns the length of the data referenced by each * capability ID fixed up to occupy an even multiple of * four-bytes. */ m_cbDataMemory += lpAppCapData->pCapID->LockCapabilityIdentifierData(); /* * Add up the space required to hold the application * data octet strings if they are present. Make sure * there is enough space for each octet string to occupy * an even multiple of four bytes. Add room to hold the * actual octet string structure also since the * capability structure only contains a pointer to a OSTR */ if (lpAppCapData->poszAppData != NULL) { m_cbDataMemory += ROUNDTOBOUNDARY(sizeof(OSTR)); m_cbDataMemory += ROUNDTOBOUNDARY(lpAppCapData->poszAppData->length); } } } } } /* * Determine the amount of memory necessary to hold all of the pointers * to the non-collapsing capabilities as well as the actual * GCCNonCollapsingCapability structures. */ m_cbDataMemory += number_of_capabilities * (sizeof (PGCCNonCollapsingCapability) + ROUNDTOBOUNDARY( sizeof(GCCNonCollapsingCapability)) ); /* * Add the amount of memory necessary to hold the string data associated * with each capability ID. */ m_CollapsedCapListForAllNodes.Reset(); while (NULL != (lpAppCapData = m_CollapsedCapListForAllNodes.Iterate())) { m_cbDataMemory += lpAppCapData->pCapID->LockCapabilityIdentifierData(); } /* * Add the memory to hold the application capability pointers * and structures. */ number_of_capabilities = m_CollapsedCapListForAllNodes.GetCount(); m_cbDataMemory += number_of_capabilities * (sizeof (PGCCApplicationCapability) + ROUNDTOBOUNDARY( sizeof(GCCApplicationCapability)) ); } return m_cbDataMemory; } /* * UINT GetAppRoster() * * Public Function Description * This routine is used to obtain a pointer to the GCCApplicatonRoster. * This routine should not be called before LockApplicationRoster is * called. LockApplicationRoster will create the GCCApplicationRoster in * the memory provided. The GCCApplicationRoster is what is delivered to * the end user Application SAP. */ UINT CAppRoster::GetAppRoster( PGCCApplicationRoster pGccAppRoster, LPBYTE pData) { UINT rc; DebugEntry(CAppRoster::GetAppRoster); if (GetLockCount() > 0) { UINT data_length; /* * Fill in the output length parameter which indicates how much data * referenced outside the structure will be written. */ rc = m_cbDataMemory; /* * Get the data associated with the roster's session key and save * the length of the data written into memory. */ data_length = m_pSessionKey->GetGCCSessionKeyData(&pGccAppRoster->session_key, pData); /* * Move the memory pointer past the data associated with the * session key. */ pData += data_length; /* * Fill in other roster structure elements. */ pGccAppRoster->application_roster_was_changed = m_fRosterHasChanged; pGccAppRoster->instance_number = (USHORT) m_nInstance; pGccAppRoster->nodes_were_added = m_fPeerEntitiesAdded; pGccAppRoster->nodes_were_removed = m_fPeerEntitiesRemoved; pGccAppRoster->capabilities_were_changed = m_fCapabilitiesHaveChanged; /* * Fill in the full set of application roster records. */ data_length = GetApplicationRecords(pGccAppRoster, pData); /* * Move the memory pointer past the application records and their * associated data. Get the full set of application capabilities. */ pData += data_length; data_length = GetCapabilitiesList(pGccAppRoster, pData); } else { ERROR_OUT(("CAppRoster::GetAppRoster: Error data not locked")); rc = 0; } return rc; } /* * void UnLockApplicationRoster () * * Public Function Description * This member function is responsible for unlocking the data locked for * the "API" application roster after the lock count goes to zero. */ void CAppRoster::UnLockApplicationRoster() { DebugEntry(CAppRoster::UnLockApplicationRoster); if (Unlock(FALSE) == 0) { // reset the size m_cbDataMemory = 0; // free up all the memory locked for "API" data. APP_NODE_RECORD *lpAppNodeRecord; APP_RECORD *lpAppRecData; APP_CAP_ITEM *lpAppCapData; CAppRecordList2 *lpAppRecDataList; // unlock session key data m_pSessionKey->UnLockSessionKeyData(); // iterate through all the node records m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRecord = m_NodeRecordList2.Iterate())) { // iterate through this node's record list lpAppNodeRecord->AppRecordList.Reset(); while (NULL != (lpAppRecData = lpAppNodeRecord->AppRecordList.Iterate())) { // set up an iterator for the list of non-collapsing // capabilities held within each application roster. lpAppRecData->non_collapsed_caps_list.Reset(); while (NULL != (lpAppCapData = lpAppRecData->non_collapsed_caps_list.Iterate())) { lpAppCapData->pCapID->UnLockCapabilityIdentifierData(); } } // iterate through this node's sub-node record list lpAppNodeRecord->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRecord->SubNodeList2.Iterate())) { lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate())) { // set up an iterator for the list of non-collapsing // capabilities held within each application roster. lpAppRecData->non_collapsed_caps_list.Reset(); while (NULL != (lpAppCapData = lpAppRecData->non_collapsed_caps_list.Iterate())) { lpAppCapData->pCapID->UnLockCapabilityIdentifierData(); } } } } // iterate through collapsed caps m_CollapsedCapListForAllNodes.Reset(); while (NULL != (lpAppCapData = m_CollapsedCapListForAllNodes.Iterate())) { lpAppCapData->pCapID->UnLockCapabilityIdentifierData(); } } // we have to call Release() because we used Unlock(FALSE) Release(); } /* * UINT GetApplicationRecords () * * Private Function Description * This routine inserts the complete set of application roster records * into the passed in application roster structure. * * Formal Parameters * gcc_roster - (o) GCCApplicationRoster to be filled in. * memory - (o) Location in memory to begin writing records. * * Return Value * The total amount of data written into memory. * * Side Effects * The memory pointer passed in will be advanced by the amount of memory * necessary to hold the application records and their data. * * Caveats * none */ UINT CAppRoster::GetApplicationRecords( PGCCApplicationRoster gcc_roster, LPBYTE memory) { UINT data_length = 0; UINT record_count = 0; PGCCApplicationRecord gcc_record; UINT capability_data_length; APP_NODE_RECORD *lpAppNodeRec; CAppRecordList2 *lpAppRecDataList; APP_RECORD *lpAppRecData; UserID uid, uid2; EntityID eid; DebugEntry(CAppRoster::GetApplicationRecords); /* * Initialize the number of records in the roster to zero. */ gcc_roster->number_of_records = 0; /* * First calculate the total number of records. This count is used to * allocate the space necessary to hold the record pointers. Note that we * must count both the application record list and the sub-node list. */ m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate())) { /* * Add the number of application records at this node to the count. */ gcc_roster->number_of_records += (USHORT) (lpAppNodeRec->AppRecordList.GetCount()); /* * Next add the number of sub node entries. */ if (! lpAppNodeRec->SubNodeList2.IsEmpty()) { lpAppNodeRec->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRec->SubNodeList2.Iterate())) { gcc_roster->number_of_records += (USHORT) (lpAppRecDataList->GetCount()); } } } if (gcc_roster->number_of_records != 0) { /* * Fill in the roster's pointer to the list of application record * pointers. The pointer list will begin at the memory location passed * into this routine. */ gcc_roster->application_record_list = (PGCCApplicationRecord *)memory; /* * Move the memory pointer past the list of record pointers. This is * where the first application record will be written. */ memory += gcc_roster->number_of_records * sizeof(PGCCApplicationRecord); /* * Add to the data length the amount of memory necessary to hold the * application record pointers. Go ahead and add the amount of memory * necessary to hold all of the GCCApplicationRecord structures. */ data_length += gcc_roster->number_of_records * (sizeof(PGCCApplicationRecord) + ROUNDTOBOUNDARY(sizeof(GCCApplicationRecord))); record_count = 0; m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate(&uid))) { /* * Iterate through this node's record list, building an "API" * application record for each record in the list. */ lpAppNodeRec->AppRecordList.Reset(); while (NULL != (lpAppRecData = lpAppNodeRec->AppRecordList.Iterate(&eid))) { /* * Set the application record pointer equal to the location in * memory where it will be written. */ gcc_record = (PGCCApplicationRecord)memory; /* * Save the pointer to the application record in the roster's * list of record pointers. */ gcc_roster->application_record_list[record_count] = gcc_record; /* * Get the GCC node ID from the node iterator. */ gcc_record->node_id = uid; /* * Get the Entity ID from the record iterator. */ gcc_record->entity_id = eid; /* * Fill in other application record elements. */ gcc_record->is_enrolled_actively = lpAppRecData->is_enrolled_actively; gcc_record->is_conducting_capable = lpAppRecData->is_conducting_capable; gcc_record->startup_channel_type = lpAppRecData->startup_channel_type; gcc_record->application_user_id = lpAppRecData->application_user_id; /* * Advance the memory pointer past the application record * structure. This is where the list of non-collapsing * capabilities pointers will begin. Round the memory location * off to fall on an even four-byte boundary. */ memory += ROUNDTOBOUNDARY(sizeof(GCCApplicationRecord)); /* * Fill in the non-collapsing capabilities for this application * record. */ capability_data_length = GetNonCollapsedCapabilitiesList( gcc_record, &lpAppRecData->non_collapsed_caps_list, memory); /* * Add the amount of memory necessary to hold the list of * capabilities and associated data to the overall length and * move the memory pointer past the capabilities data. */ memory += capability_data_length; data_length += capability_data_length; /* * Increment the record list array counter. */ record_count++; } /* * Iterate through this node's sub-node record list, building an * "API" application record for each record in the list. */ lpAppNodeRec->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRec->SubNodeList2.Iterate(&uid2))) { /* * Iterate through this node's record list. */ lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate(&eid))) { /* * Set the application record pointer equal to the location * in memory where it will be written. */ gcc_record = (PGCCApplicationRecord)memory; /* * Save the pointer to the application record in the * roster's list of record pointers. */ gcc_roster->application_record_list[record_count] = gcc_record; /* * Get the node ID from the sub_node_iterator. */ gcc_record->node_id = uid2; /* * Get the entity ID from the record_iterator. */ gcc_record->entity_id = eid; /* * Fill in other application record elements. */ gcc_record->is_enrolled_actively = lpAppRecData->is_enrolled_actively; gcc_record->is_conducting_capable = lpAppRecData->is_conducting_capable; gcc_record->startup_channel_type = lpAppRecData->startup_channel_type; gcc_record->application_user_id = lpAppRecData->application_user_id; /* * Advance the memory pointer past the application record * structure. This is where the list of non-collapsing * capabilities pointers will begin. Round the memory * location off to fall on an even four-byte boundary. */ memory += ROUNDTOBOUNDARY(sizeof(GCCApplicationRecord)); /* * Fill in the non-collapsing capabilities for this * application record. The memory pointer will be advanced * past the capabilities list and data. */ capability_data_length = GetNonCollapsedCapabilitiesList( gcc_record, &lpAppRecData->non_collapsed_caps_list, memory); /* * Add the amount of memory necessary to hold the list of * capabilities and associated data to the overall length. */ memory += capability_data_length; data_length += capability_data_length; /* * Increment the record list array counter. */ record_count++; } } } } else { /* * There were no application records so set the pointer to the list * of records to NULL and the data_length return value to zero. */ gcc_roster->application_record_list = NULL; data_length = 0; } return (data_length); } /* * UINT GetCapabilitiesList () * * Private Function Description * This routine fills in the capabilities portion of the * GCCAppicationRoster structure. * * Formal Parameters * gcc_roster - (o) GCCApplicationRoster to be filled in * memory - (o) Location in memory to begin writing records. * * Return Value * The total amount of data written into memory. * * Side Effects * none * * Caveats * none */ UINT CAppRoster::GetCapabilitiesList( PGCCApplicationRoster gcc_roster, LPBYTE memory) { UINT data_length = 0; UINT capability_id_data_length = 0; UINT capability_count; PGCCApplicationCapability gcc_capability; APP_CAP_ITEM *lpAppCapData; DebugEntry(CAppRoster::GetCapabilitiesList); /* * Retrieve the number of capabilities for this roster and fill in any that * are present. */ gcc_roster->number_of_capabilities = (USHORT) m_CollapsedCapListForAllNodes.GetCount(); if (gcc_roster->number_of_capabilities != 0) { /* * Fill in the roster's pointer to the list of application capability * pointers. The pointer list will begin at the memory location passed * into this routine. */ gcc_roster->capabilities_list = (PGCCApplicationCapability *)memory; /* * Move the memory pointer past the list of capability pointers. This * is where the first application capability structure will be written. */ memory += (Int)(gcc_roster->number_of_capabilities * sizeof(PGCCApplicationCapability)); /* * Add to the data length the amount of memory necessary to hold the * application capability pointers. Go ahead and add the amount of * memory necessary to hold all of the GCCApplicationCapability * structures. */ data_length += gcc_roster->number_of_capabilities * (sizeof(PGCCApplicationCapability) + ROUNDTOBOUNDARY ( sizeof(GCCApplicationCapability)) ); capability_count = 0; m_CollapsedCapListForAllNodes.Reset(); while (NULL != (lpAppCapData = m_CollapsedCapListForAllNodes.Iterate())) { /* * Set the application capability pointer equal to the * location in memory where it will be written. */ gcc_capability = (PGCCApplicationCapability)memory; /* * Save the pointer to the application capability in the roster's * list of application capability pointers. */ gcc_roster->capabilities_list[capability_count] = gcc_capability; /* * Advance the memory pointer past the application capability * structure. This is where the string data for the capability ID * will be written. Ensure that the memory pointer falls on an * even four-byte boundary. */ memory += (Int)(ROUNDTOBOUNDARY(sizeof(GCCApplicationCapability))); /* * Retrieve the capability ID information from the internal * CapabilityIDData object. The length returned by this call will * have already been rounded to an even multiple of four bytes. */ capability_id_data_length = lpAppCapData->pCapID->GetGCCCapabilityIDData( &gcc_capability->capability_id, memory); /* * Advance the memory pointer past the string data written into * memory by the capability ID object. Add the length of the string * data to the overall capability length. */ memory += (Int)capability_id_data_length; data_length += capability_id_data_length; /* * Now fill in the rest of the capability. */ gcc_capability->capability_class.eType =lpAppCapData->eCapType; if (gcc_capability->capability_class.eType == GCC_UNSIGNED_MINIMUM_CAPABILITY) { gcc_capability->capability_class.nMinOrMax = lpAppCapData->nUnsignedMinimum; } else if (gcc_capability->capability_class.eType == GCC_UNSIGNED_MAXIMUM_CAPABILITY) { gcc_capability->capability_class.nMinOrMax = lpAppCapData->nUnsignedMaximum; } gcc_capability->number_of_entities = lpAppCapData->cEntries; /* * Increment the capability ID array counter. */ capability_count++; } } else { gcc_roster->capabilities_list = NULL; } return (data_length); } /* * UINT GetNonCollapsedCapabilitiesList () * * Private Function Description: * This routine is used to fill in the "API" non-collapsing capabilities * portion of a GCCApplicationRoster from the data which is stored * internally by this class. * * Formal Parameters * gcc_record - (o) The application record to be filled in. * pAppCapItemList (i) The internal capabilities data. * memory (i/o) The memory location to begin writing data. * * Return Value * The total amount of data written into memory. * * Side Effects * The memory pointer passed in will be advanced by the amount of memory * necessary to hold the capabilities and their data. * * Caveats * none */ UINT CAppRoster::GetNonCollapsedCapabilitiesList( PGCCApplicationRecord gcc_record, CAppCapItemList *pAppCapItemList, LPBYTE memory) { UINT capability_count; PGCCNonCollapsingCapability gcc_capability; APP_CAP_ITEM *lpAppCapData; UINT capability_id_length = 0; UINT capability_data_length = 0; DebugEntry(CAppRoster::GetNonCollapsedCapabilitiesList); /* * Get the number of non-collapsed capabilities. */ gcc_record->number_of_non_collapsed_caps = (USHORT) pAppCapItemList->GetCount(); if (gcc_record->number_of_non_collapsed_caps != 0) { /* * Fill in the record's pointer to the list of non-collapsing * capabilities pointers. The pointer list will begin at the memory * location passed into this routine. */ gcc_record->non_collapsed_caps_list = (PGCCNonCollapsingCapability *)memory; /* * Move the memory pointer past the list of capability pointers. This * is where the first capability structure will be written. */ memory += (Int)(gcc_record->number_of_non_collapsed_caps * sizeof(PGCCNonCollapsingCapability)); /* * Add to the data length the amount of memory necessary to hold the * capability pointers. Go ahead and add the amount of memory necessary * to hold all of the GCCNonCollapsingCapability structures. */ capability_data_length = gcc_record->number_of_non_collapsed_caps * (sizeof(PGCCNonCollapsingCapability) + ROUNDTOBOUNDARY(sizeof (GCCNonCollapsingCapability))); /* * Iterate through this record's capabilities list, building an "API" * non-collapsing capability for each capability in the list. */ capability_count = 0; pAppCapItemList->Reset(); while (NULL != (lpAppCapData = pAppCapItemList->Iterate())) { /* * Set the capability pointer equal to the location in memory where * it will be written. */ gcc_capability = (PGCCNonCollapsingCapability)memory; /* * Save the pointer to the capability in the record's list of * capability pointers. */ gcc_record->non_collapsed_caps_list[capability_count] = gcc_capability; /* * Move the memory pointer past the capability ID structure. This * is where the data associated with the structure will be written. * Retrieve the capability ID data from the internal object, saving * it in the "API" capability ID structure. */ memory += (Int)ROUNDTOBOUNDARY(sizeof(GCCNonCollapsingCapability)); capability_id_length = lpAppCapData->pCapID->GetGCCCapabilityIDData( &gcc_capability->capability_id, memory); /* * Add to the data length the amount of memory necessary to hold the * capability ID data. */ capability_data_length += capability_id_length; /* * Move the memory pointer past the data filled in for the * capability ID. This is where the application data OSTR * contained in the non-collapsing capability will be written, if * one exists. Note that the capability contains a pointer to a * OSTR and therefore the OSTR structure as well * as the string data must be written into memory. */ memory += capability_id_length; if (lpAppCapData->poszAppData != NULL) { /* * Set the application data structure pointer equal to the * location in memory where it will be written. */ gcc_capability->application_data = (LPOSTR) memory; gcc_capability->application_data->length = lpAppCapData->poszAppData->length; /* * Move the memory pointer past the OSTR structure * and round it off to an even four-byte boundary. This is * where the actual string data will be written so set the * structure string pointer equal to that location. */ memory += ROUNDTOBOUNDARY(sizeof(OSTR)); gcc_capability->application_data->value =(LPBYTE)memory; /* * Copy the actual application string data into memory. */ ::CopyMemory(gcc_capability->application_data->value, lpAppCapData->poszAppData->value, lpAppCapData->poszAppData->length); /* * Add to the data length the amount of memory necessary to * hold the application data structure and string. The lengths * will need to be aligned on a four-byte boundary before * adding them to the total length. */ capability_data_length += ROUNDTOBOUNDARY(sizeof(OSTR)); capability_data_length += ROUNDTOBOUNDARY(gcc_capability->application_data->length); /* * Move the memory pointer past the application string data. * The memory pointer is then fixed up to ensure that it falls * on an even four-byte boundary. */ memory += ROUNDTOBOUNDARY(lpAppCapData->poszAppData->length); } else { gcc_capability->application_data = NULL; } /* * Increment the capability array counter. */ capability_count++; } } else { gcc_record->non_collapsed_caps_list = NULL; capability_data_length = 0; } return (capability_data_length); } /* * void FreeApplicationRosterData () * * Private Function Description: * This routine is used to free up any data which was locked for an "API" * application roster. * * Formal Parameters * none * * Return Value * none * * Side Effects * none * * Caveats * none */ void CAppRoster::FreeApplicationRosterData(void) { APP_NODE_RECORD *lpAppNodeRec; APP_RECORD *lpAppRecData; APP_CAP_ITEM *lpAppCapData; CAppRecordList2 *lpAppRecDataList; DebugEntry(CAppRoster::FreeApplicationRosterData); m_pSessionKey->UnLockSessionKeyData(); /* * Unlock the data associated with each non-collapsed capability by * iterating through the list of application records at each node as well as * the list of sub-node records at each node, calling "UnLock" for each * CapabilityIDData associated with each cabability. */ m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate())) { lpAppNodeRec->AppRecordList.Reset(); while (NULL != (lpAppRecData = lpAppNodeRec->AppRecordList.Iterate())) { lpAppRecData->non_collapsed_caps_list.Reset(); while (NULL != (lpAppCapData = lpAppRecData->non_collapsed_caps_list.Iterate())) { lpAppCapData->pCapID->UnLockCapabilityIdentifierData (); } } lpAppNodeRec->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRec->SubNodeList2.Iterate())) { lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate())) { lpAppRecData->non_collapsed_caps_list.Reset(); while (NULL != (lpAppCapData = lpAppRecData->non_collapsed_caps_list.Iterate())) { lpAppCapData->pCapID->UnLockCapabilityIdentifierData(); } } } } /* * Iterate through the list of collapsed capabilities, unlocking the data * for each CapabilityIDData object associated with each capability. */ m_CollapsedCapListForAllNodes.Reset(); while (NULL != (lpAppCapData = m_CollapsedCapListForAllNodes.Iterate())) { lpAppCapData->pCapID->UnLockCapabilityIdentifierData(); } } /* * GCCError AddRecord () * * Public Function Description * This member function is responsible for inserting a new application * record into the Roster. This routine will return a failure if the * application record already exist. * * Caveats * Note that it is possible for a roster record (not application record) * to already exist at this node if this is the second application * entity to enroll at this node. */ GCCError CAppRoster:: AddRecord(GCCEnrollRequest *pReq, GCCNodeID nid, GCCEntityID eid) { GCCError rc = GCC_NO_ERROR; APP_NODE_RECORD *node_record; APP_RECORD *pAppRecord; CAppCapItemList *pAppCapItemList; DebugEntry(CAppRoster::AddRecord); if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU (); m_fPduIsFlushed = FALSE; } /* * First create a roster entry for this user ID if one does not exists. */ if (NULL == (node_record = m_NodeRecordList2.Find(nid))) { DBG_SAVE_FILE_LINE node_record = new APP_NODE_RECORD; if (node_record != NULL) { m_NodeRecordList2.Append(nid, node_record); } else { ERROR_OUT(("CAppRoster: AddRecord: Resource Error Occured")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } else { WARNING_OUT(("CAppRoster: AddRecord: Node Record is found")); } /* * Check to make sure that the application record does not already exist.. */ if ((NULL != node_record->AppRecordList.Find(eid)) || (NULL != node_record->ListOfAppCapItemList2.Find(eid))) { WARNING_OUT(("AppRoster: AddRecord: Record already exists")); rc = GCC_INVALID_PARAMETER; goto MyExit; } // Next create a record entry in the roster's app_record_list. DBG_SAVE_FILE_LINE pAppRecord = new APP_RECORD; if (NULL == pAppRecord) { ERROR_OUT(("CAppRoster: AddRecord: can't create APP_RECORD")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } /* ** Here we must determine if an entry already exists at this ** node. If so, only one entry can be conducting capable at a ** node. Therefore, we set this variable based on this. We use ** the "was_conducting_capable" variable to keep up with the ** original state incase the conducting capable node leaves the ** conference. */ pAppRecord->is_conducting_capable = pReq->fConductingCapable; APP_RECORD *p; node_record->AppRecordList.Reset(); while (NULL != (p = node_record->AppRecordList.Iterate())) { if (p->is_conducting_capable) { pAppRecord->is_conducting_capable = FALSE; break; } } pAppRecord->was_conducting_capable = pReq->fConductingCapable; pAppRecord->is_enrolled_actively = pReq->fEnrollActively; pAppRecord->startup_channel_type = pReq->nStartupChannelType; pAppRecord->application_user_id = pReq->nUserID; if (pReq->cNonCollapsedCaps != 0) { rc = AddNonCollapsedCapabilities ( &pAppRecord->non_collapsed_caps_list, pReq->cNonCollapsedCaps, pReq->apNonCollapsedCaps); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::AddRecord: can't add non collapsed caps, rc=%u", (UINT) rc)); delete pAppRecord; goto MyExit; } } // Add the new record to the list of records at this node node_record->AppRecordList.Append(eid, pAppRecord); // from now on, we cannot free pAppRecord in case of error, // because it is now in the app record list. // Increment the instance number. m_nInstance++; m_fPeerEntitiesAdded = TRUE; m_fRosterHasChanged = TRUE; // Add an update to the PDU. rc = BuildApplicationRecordListPDU(APP_ADD_RECORD, nid, eid); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::AddRecord: can't build app record list, rc=%u", (UINT) rc)); goto MyExit; } if (pReq->cCollapsedCaps != 0) { /* ** Create a new capabilities list and insert it into the roster ** record list of capabilities. */ DBG_SAVE_FILE_LINE pAppCapItemList = new CAppCapItemList; if (NULL == pAppCapItemList) { ERROR_OUT(("CAppRoster::AddRecord: can't create CAppCapItemList")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } rc = AddCollapsableCapabilities(pAppCapItemList, pReq->cCollapsedCaps, pReq->apCollapsedCaps); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::AddRecord: can't add collapsable caps, rc=%u", (UINT) rc)); delete pAppCapItemList; goto MyExit; } // Add list of capabilities to list at this node node_record->ListOfAppCapItemList2.Append(eid, pAppCapItemList); m_fCapabilitiesHaveChanged = TRUE; // from now on, we cannot free pAppCapItemList in case of error, // because it is now in the app cap item list // Rebuild the collapsed capabilities list. MakeCollapsedCapabilitiesList(); // Build the capabilities refresh portion of the PDU. rc = BuildSetOfCapabilityRefreshesPDU(); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::AddRecord: can't build set of cap refresh, rc=%u", (UINT) rc)); goto MyExit; } } MyExit: DebugExitINT(CAppRoster::AddRecord, rc); return rc; } /* * GCCError AddCollapsableCapabilities () * * Private Function Description * This routine takes API collapsed capabilities list data passed in * through a local request and converts it to internal collapsed * capabillities. * * Formal Parameters * pAppCapItemList - (o) Pointer to internal capabilites list * to fill in. * number_of_capabilities - (i) Number of capabilities in the source * list. * capabilities_list - (i) Pointer to source capabilities list. * * Return Value * GCC_NO_ERROR - No error occured. * GCC_ALLOCATION_FAILURE - A resource error occured. * * Side Effects * The collapsed capabilities will be recalculated at this node after * all the new caps are added. * * Caveats * none */ GCCError CAppRoster::AddCollapsableCapabilities ( CAppCapItemList *pAppCapItemList, UINT number_of_capabilities, PGCCApplicationCapability *capabilities_list) { GCCError rc = GCC_NO_ERROR; APP_CAP_ITEM *pAppCapItem; UINT i; BOOL capability_already_exists; DebugEntry(CAppRoster::AddCollapsableCapabilities); for (i = 0; i < number_of_capabilities; i++) { DBG_SAVE_FILE_LINE pAppCapItem = new APP_CAP_ITEM((GCCCapabilityType) capabilities_list[i]->capability_class.eType); if (pAppCapItem != NULL) { DBG_SAVE_FILE_LINE pAppCapItem->pCapID = new CCapIDContainer(&capabilities_list[i]->capability_id, &rc); if ((pAppCapItem->pCapID != NULL) && (rc == GCC_NO_ERROR)) { APP_CAP_ITEM *lpAppCapData; /* ** Here we check to make sure that this capability id does ** not alreay exists in the list. */ capability_already_exists = FALSE; pAppCapItemList->Reset(); while (NULL != (lpAppCapData = pAppCapItemList->Iterate())) { if (*lpAppCapData->pCapID == *pAppCapItem->pCapID) { capability_already_exists = TRUE; delete pAppCapItem; break; } } if (capability_already_exists == FALSE) { if (capabilities_list[i]->capability_class.eType == GCC_UNSIGNED_MINIMUM_CAPABILITY) { pAppCapItem->nUnsignedMinimum = capabilities_list[i]->capability_class.nMinOrMax; } else if (capabilities_list[i]->capability_class.eType == GCC_UNSIGNED_MAXIMUM_CAPABILITY) { pAppCapItem->nUnsignedMaximum = capabilities_list[i]->capability_class.nMinOrMax; } // Since we have yet to collapse the capabilities set to 1 pAppCapItem->cEntries = 1; // Add this capability to the list pAppCapItemList->Append(pAppCapItem); } } else if (pAppCapItem->pCapID == NULL) { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } else { goto MyExit; } } else { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } MyExit: if (GCC_NO_ERROR != rc) { delete pAppCapItem; } return rc; } /* * GCCError AddNonCollapsedCapabilities () * * Private Function Description * This routine takes API non-collapsed capabilities list data passed in * through a local request and converts it to internal non-collapsed * capabillities. * * Formal Parameters * pAppCapItemList - (o) Pointer to internal non-collapsed * capabilites list to fill in. * number_of_capabilities - (i) Number of non-collapsed capabilities in * the source list. * capabilities_list - (i) Pointer to source non-collapsed * capabilities list. * * Return Value * GCC_NO_ERROR - No error occured. * GCC_INVALID_NON_COLLAPSED_CAP - Invalid non-collapsed capability. * GCC_ALLOCATION_FAILURE - A resource error occured. * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::AddNonCollapsedCapabilities ( CAppCapItemList *pAppCapItemList, UINT number_of_capabilities, PGCCNonCollapsingCapability *capabilities_list) { GCCError rc = GCC_NO_ERROR; APP_CAP_ITEM *pAppCapItem = NULL; UINT i; DebugEntry(CAppRoster::AddNonCollapsedCapabilities); for (i = 0; i < number_of_capabilities; i++) { // // LONCHANC: Cap type is not set here. // for now, it is zero. // DBG_SAVE_FILE_LINE pAppCapItem = new APP_CAP_ITEM((GCCCapabilityType) 0); if (pAppCapItem != NULL) { DBG_SAVE_FILE_LINE pAppCapItem->pCapID = new CCapIDContainer(&capabilities_list[i]->capability_id, &rc); if (pAppCapItem->pCapID != NULL) { if (capabilities_list[i]->application_data != NULL) { if (NULL == (pAppCapItem->poszAppData = ::My_strdupO2( capabilities_list[i]->application_data->value, capabilities_list[i]->application_data->length))) { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } else if (pAppCapItem->poszAppData->length > MAXIMUM_APPLICATION_DATA_LENGTH) { rc = GCC_INVALID_NON_COLLAPSED_CAP; goto MyExit; } } // Add this capability to the list if no errors if( !pAppCapItemList->Append(pAppCapItem) ) { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } else { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } else { rc = GCC_ALLOCATION_FAILURE; goto MyExit; } } MyExit: if (GCC_NO_ERROR != rc) { if(pAppCapItem != NULL) { delete pAppCapItem; } } return rc; } /* * GCCError RemoveRecord () * * Public Function Description * This member function completely removes the specified record from the * application roster. This includes any capabilities associated with * this record. It also takes care of keeping the Instance number and * added and removed flags up to date. */ GCCError CAppRoster::RemoveRecord(GCCNodeID nid, GCCEntityID eid) { GCCError rc; APP_RECORD *pAppRecord; APP_NODE_RECORD *node_record; DebugEntry(CAppRoster::RemoveRecord); if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU (); m_fPduIsFlushed = FALSE; } // First see if the record is contained in the Roster_Record_List. if (NULL == (node_record = m_NodeRecordList2.Find(nid))) { TRACE_OUT(("CAppRoster::RemoveRecord: can't find node record, nid=%u", (UINT) nid)); rc = GCC_INVALID_PARAMETER; goto MyExit; } if (NULL == (pAppRecord = node_record->AppRecordList.Find(eid))) { TRACE_OUT(("CAppRoster::RemoveRecord: can't find app record, eid=%u", (UINT) eid)); rc = GCC_INVALID_PARAMETER; goto MyExit; } /* ** Here we must determine if any of the remaining APEs at this ** node should become conducting capable based on their role ** at the time they enrolled. We only do this if the record ** that is being deleted was conducting capabile. */ if (pAppRecord->is_conducting_capable) { APP_RECORD *p; EntityID eid2; node_record->AppRecordList.Reset(); while (NULL != (p = node_record->AppRecordList.Iterate(&eid2))) { /* ** Here we only deal with record entries other than the ** one being removed. */ if (eid2 != eid) { if (p->was_conducting_capable) { p->is_conducting_capable = TRUE; /* ** Set up the update PDU for this conducting ** capable change. */ rc = BuildApplicationRecordListPDU(APP_REPLACE_RECORD, nid, eid2); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::RemoveRecord: can't build app record list, rc=%u", (UINT) rc)); goto MyExit; } break; } } } } // Now delete the record rc = DeleteRecord(nid, eid, TRUE); if (GCC_NO_ERROR != rc) { WARNING_OUT(("CAppRoster::RemoveRecord: can't delete record, rc=%u", (UINT) rc)); goto MyExit; } // Increment the instance number. m_nInstance++; m_fPeerEntitiesRemoved = TRUE; m_fRosterHasChanged = TRUE; // Add an update to the PDU. rc = BuildApplicationRecordListPDU(APP_DELETE_RECORD, nid, eid); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::RemoveRecord: can't build app record list, rc=%u", (UINT) rc)); goto MyExit; } /* ** If the capabilities changed during the above processing ** we must create a new collapsed capabilities list and ** build a new capability refresh PDU. */ if (m_fCapabilitiesHaveChanged) { MakeCollapsedCapabilitiesList(); rc = BuildSetOfCapabilityRefreshesPDU(); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::RemoveRecord: can't build set of cap refreshes, rc=%u", (UINT) rc)); goto MyExit; } } MyExit: DebugExitINT(CAppRoster::RemoveRecord, rc); return rc; } /* * GCCError ReplaceRecord () * * Public Function Description * This routine completely replaces the specified record's parameters * with the new parameters passed in. This includes the capabilities. */ GCCError CAppRoster:: ReplaceRecord(GCCEnrollRequest *pReq, GCCNodeID nid, GCCEntityID eid) { GCCError rc = GCC_NO_ERROR; BOOL capable_node_found; APP_NODE_RECORD *node_record; APP_RECORD *pAppRecord, *p; APP_CAP_ITEM *lpAppCapData; CAppCapItemList NonCollCapsList; DebugEntry(CAppRoster::ReplaceRecord); if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU (); m_fPduIsFlushed = FALSE; } /* ** First determine if the node record does actually already exists. If not ** we return an error here. */ if (NULL == (node_record = m_NodeRecordList2.Find(nid))) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't find the node record for nid=%u", (UINT) nid)); rc = GCC_INVALID_PARAMETER; goto MyExit; } // make sure the app record exists. if not, return an error if (NULL == (pAppRecord = node_record->AppRecordList.Find(eid))) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't find the app record for eid=%u", (UINT) eid)); rc = GCC_INVALID_PARAMETER; goto MyExit; } /* ** First check to make sure that we can build the new record before ** replacing the old record. The only entry we need to wory about ** here are the non-collapsing capabilities. */ if (pReq->cNonCollapsedCaps != 0) { rc = AddNonCollapsedCapabilities(&NonCollCapsList, pReq->cNonCollapsedCaps, pReq->apNonCollapsedCaps); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't add non collapsed caps, rc=%u", (UINT) rc)); goto MyExit; } } // Now replace the record entries. pAppRecord->is_enrolled_actively = pReq->fEnrollActively; pAppRecord->was_conducting_capable = pReq->fConductingCapable; pAppRecord->startup_channel_type = pReq->nStartupChannelType; pAppRecord->application_user_id = pReq->nUserID; /* ** If the is conducting capable flag that was passed in was set ** to FALSE we can go ahead and set the internal is conducting ** capable flag to FALSE regardless of what the previous ** setting was. If it was passed in TRUE we leave the previous ** setting alone. */ if (pAppRecord->was_conducting_capable == FALSE) { pAppRecord->is_conducting_capable = FALSE; } /* ** Here we delete the old non-collapsed capabilites and then ** add the new ones. */ if (! pAppRecord->non_collapsed_caps_list.IsEmpty()) { pAppRecord->non_collapsed_caps_list.DeleteList(); pAppRecord->non_collapsed_caps_list.Clear(); } // Copy the new non collapsed capabilities if any exists. if (pReq->cNonCollapsedCaps != 0) { while (NULL != (lpAppCapData = NonCollCapsList.Get())) { pAppRecord->non_collapsed_caps_list.Append(lpAppCapData); } } // // handling collapsing cap list // m_nInstance++; m_fRosterHasChanged = TRUE; rc = BuildApplicationRecordListPDU(APP_REPLACE_RECORD, nid, eid); if (rc != GCC_NO_ERROR) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't build app record list, rc=%u", (UINT) rc)); goto MyExit; } /* ** Here we must make sure that at least one of the APEs is ** Conducting Capable. We do this by first scanning the ** list to see if anyone is it. If one is not found, the ** same list is scanned for an APE that "was" previously ** capable. The first one found that was previously ** capable is now it. If none are found then no one is ** capable. */ capable_node_found = FALSE; node_record->AppRecordList.Reset(); while (NULL != (p = node_record->AppRecordList.Iterate())) { if (p->is_conducting_capable) { capable_node_found = TRUE; break; } } if (! capable_node_found) { GCCEntityID eid2; node_record->AppRecordList.Reset(); while (NULL != (p = node_record->AppRecordList.Iterate(&eid2))) { if (p->was_conducting_capable) { p->is_conducting_capable = TRUE; /* ** Set up the update PDU for this conducting ** capable change. */ rc = BuildApplicationRecordListPDU(APP_REPLACE_RECORD, nid, eid2); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't build app record list, rc=%u", (UINT) rc)); goto MyExit; } break; } } } /* ** This section of code deals with the collapsable capabilities. ** First we determine if the capabilities passed in are different ** from the previously existing capabilities. If so, we must ** delete the old set of caps and add back in the new ones. */ TRACE_OUT(("ApplicatonRoster:ReplaceRecord: Check to see if caps match")); if (! DoCapabilitiesListMatch(nid, eid, pReq->cCollapsedCaps, pReq->apCollapsedCaps)) { CAppCapItemList *pCollCapsList, *q; TRACE_OUT(("ApplicatonRoster:ReplaceRecord: Capabilities match")); m_fCapabilitiesHaveChanged = TRUE; /* ** Delete the old capabilities list since it does not match the ** new capabilities list. */ if (NULL != (q = node_record->ListOfAppCapItemList2.Find(eid))) { q->DeleteList(); delete q; node_record->ListOfAppCapItemList2.Remove(eid); } /* ** Here we add back in the new capabilities. Create a new ** capabilities list and insert it into the roster record list of ** capabilities. */ if (pReq->cCollapsedCaps != 0) { DBG_SAVE_FILE_LINE pCollCapsList = new CAppCapItemList; if (NULL == pCollCapsList) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't create CAppCapItemList")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } rc = AddCollapsableCapabilities(pCollCapsList, pReq->cCollapsedCaps, pReq->apCollapsedCaps); if (rc != GCC_NO_ERROR) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't add collapsed caps, rc=%u", (UINT) rc)); delete pCollCapsList; goto MyExit; } // Add list of capabilities to list at this node node_record->ListOfAppCapItemList2.Append(eid, pCollCapsList); } // Rebuild the collapsed capabilities list. MakeCollapsedCapabilitiesList(); // Build the capabilities refresh portion of the PDU. rc = BuildSetOfCapabilityRefreshesPDU(); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CAppRoster::ReplaceRecord: can't build set of cap refreshes, rc=%u", (UINT) rc)); goto MyExit; } } else { TRACE_OUT(("CAppRoster:ReplaceRecord:Capabilities match with previous record")); } MyExit: DebugExitINT(CAppRoster::ReplaceRecord, rc); return rc; } /* * GCCError DeleteRecord () * * Private Function Description * This member function completely removes the specified record from the * application roster. This includes any capabilities associated with * this record. * * Formal Parameters * node_id - (i) Node ID of record to delete. * entity_id - (i) Entity ID of record to delete. * clear_empty_records - (i) This flag indicates whether or not to * clear out the node record if it no-longer * holds data. When replacing a record we * do NOT want to do this so that we don't * lose any "unchanged" capabilities. * * Return Value * GCC_NO_ERROR - No error occured. * GCC_INVALID_PARAMETER - Record specified to delete does not exists. * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::DeleteRecord(UserID node_id, EntityID entity_id, BOOL clear_empty_records) { GCCError rc = GCC_NO_ERROR; APP_RECORD *application_record; CAppCapItemList *pAppCapItemList; CAppRecordList2 *pAppRecordList; UserID node_to_check; APP_NODE_RECORD *node_record; //APP_CAP_ITEM *lpAppCapData; APP_NODE_RECORD *lpAppNodeRec; DebugEntry(CAppRoster::DeleteRecord); // First see if the record is contained in the Roster_Record_List. if (NULL != (node_record = m_NodeRecordList2.Find(node_id))) { // Set up node id to check at bottom for empty record node_to_check = node_id; // Delete the application record. if (NULL != (application_record = node_record->AppRecordList.Find(entity_id))) { TRACE_OUT(("AppRoster: DeleteRecord: Delete AppRecord")); // Delete the data associated with the application record DeleteApplicationRecordData (application_record); // Remove record from application record list node_record->AppRecordList.Remove(entity_id); /* ** Delete the associated capabilities list. Note that this list ** only exists for records of local nodes. The collapsed ** capabilities list at the root node record take create of ** subordniate nodes and is deleted some where else. */ if (NULL != (pAppCapItemList = node_record->ListOfAppCapItemList2.Find(entity_id))) { m_fCapabilitiesHaveChanged = TRUE; pAppCapItemList->DeleteList(); TRACE_OUT(("AppRoster: DeleteRecord: Delete Capabilities")); delete pAppCapItemList; node_record->ListOfAppCapItemList2.Remove(entity_id); } } else { WARNING_OUT(("AppRoster: DeleteRecord: can't find this eid=%u", (UINT) entity_id)); rc = GCC_INVALID_PARAMETER; } } else { UserID uid2; /* ** Here we search through all the sub node list trying to find the ** record. Set return value to record does not exist here and ** after the record is found set it back to no error. */ rc = GCC_INVALID_PARAMETER; m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate(&uid2))) { // Delete the sub_node list if it exists if (NULL != (pAppRecordList = lpAppNodeRec->SubNodeList2.Find(node_id))) { // Delete the app_record_list entry. if (NULL != (application_record = pAppRecordList->Find(entity_id))) { // Delete the data associated with the application record DeleteApplicationRecordData (application_record); pAppRecordList->Remove(entity_id); if (pAppRecordList->IsEmpty()) { TRACE_OUT(("AppRoster: DeleteRecord: Deleting Sub-Node")); delete pAppRecordList; lpAppNodeRec->SubNodeList2.Remove(node_id); } // Set up node id to check at bottom for empty record node_to_check = uid2; rc = GCC_NO_ERROR; } break; } } } /* ** If the record list is empty and the sub node list is empty ** we can remove this entire record from the application roster. ** If the record list is empty but the sub node list is not we ** must keep the roster record around to maintain the sub node list. */ if ((rc == GCC_NO_ERROR) && clear_empty_records) { if (NULL != (node_record = m_NodeRecordList2.Find(node_to_check)) && node_record->AppRecordList.IsEmpty() && node_record->SubNodeList2.IsEmpty()) { if (! node_record->CollapsedCapList.IsEmpty()) { m_fCapabilitiesHaveChanged = TRUE; // Delete the collapsed capabilities list. node_record->CollapsedCapList.DeleteList(); } delete node_record; m_NodeRecordList2.Remove(node_to_check); } } return rc; } /* * GCCError RemoveUserReference () * * Public Function Description * This routine will only remove the application record and its sub nodes * if the node being removed is directly connected to this node. * Otherwise, we wait to receive the update from either the sub node or * the Top Provider. */ GCCError CAppRoster::RemoveUserReference(UserID detached_node) { GCCError rc = GCC_NO_ERROR; DebugEntry(CAppRoster::RemoveUserReference); // Clear out any previously allocated PDUs if (m_fPduIsFlushed) { FreeRosterUpdateIndicationPDU (); m_fPduIsFlushed = FALSE; } /* ** First Try to remove the node record if one exist. If it does not ** exist we return immediately. If it does exists we will build the ** appropriate PDU and update the instance variables. */ rc = ClearNodeRecordFromList (detached_node); if (rc == GCC_NO_ERROR) { // Increment the instance number. m_nInstance++; m_fPeerEntitiesRemoved = TRUE; m_fRosterHasChanged = TRUE; /* ** Go ahead and do the full refresh here since we do not know the ** specifics about who was deleted. */ rc = BuildApplicationRecordListPDU(APP_FULL_REFRESH, 0, 0); if (m_fCapabilitiesHaveChanged && (rc == GCC_NO_ERROR)) { // Create a new collapsed capabilities list. MakeCollapsedCapabilitiesList(); // Build the capabilities refresh portion of the PDU. rc = BuildSetOfCapabilityRefreshesPDU (); } } return rc; } /* * void DeleteApplicationRecordData () * * Private Function Description * This routine internal application record data. * * Formal Parameters * application_record - Pointer to the application record data to * delete. * * Return Value * none * * Side Effects * none * * Caveats * none */ void CAppRoster::DeleteApplicationRecordData(APP_RECORD *pAppRec) { pAppRec->non_collapsed_caps_list.DeleteList(); delete pAppRec; } /* * USHORT GetNumberOfApplicationRecords () * * Public Function Description * This routine returns the total number of application records that exist * in this application roster. * * Formal Parameters * none * * Return Value * Number of application roster records * * Side Effects * none * * Caveats * none */ UINT CAppRoster::GetNumberOfApplicationRecords(void) { UINT cRecords = 0; APP_NODE_RECORD *lpAppNodeRec; CAppRecordList2 *lpAppRecDataList; DebugEntry(CAppRoster::GetNumberOfApplicationRecords); /* ** First calculate the total number of records. This count is used to ** allocate the space necessary to hold the records. Note that we must ** count both the application record list and the sub-node list. */ m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate())) { // Add the application records at this node to the count. cRecords += lpAppNodeRec->AppRecordList.GetCount(); // Next count the sub node entries. if (! lpAppNodeRec->SubNodeList2.IsEmpty()) { lpAppNodeRec->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRec->SubNodeList2.Iterate())) { cRecords += lpAppRecDataList->GetCount(); } } } return cRecords; } /* * PGCCSessionKey GetSessionKey () * * Public Function Description * This routine returns the session key associated with this * application roster. * * Formal Parameters * none * * Return Value * PGCCSessionKey - the application key associated with this roster * * Side Effects * none * * Caveats * none */ /* * void ResetApplicationRoster () * * Public Function Description * This routine takes care of resetting all the internal flags that are * used to convey the current state of the application roster. Should be * called after the roster is flushed and any roster update messages have * been delivered (after a change to the roster occurs). */ void CAppRoster::ResetApplicationRoster(void) { m_fCapabilitiesHaveChanged = FALSE; m_fRosterHasChanged = FALSE; m_fPeerEntitiesRemoved = FALSE; m_fPeerEntitiesAdded = FALSE; } /* * BOOL DoesRecordExist () * * Public Function Description * This routine informs the caller if the specified application record * exists or not. */ BOOL CAppRoster::DoesRecordExist(UserID node_id, EntityID entity_id) { BOOL rc = FALSE; APP_NODE_RECORD *node_record; CAppRecordList2 *record_list; DebugEntry(CAppRoster::DoesRecordExist); if (NULL != (node_record = m_NodeRecordList2.Find(node_id))) { if (node_record->AppRecordList.Find(entity_id)) rc = TRUE; } else { m_NodeRecordList2.Reset(); while (NULL != (node_record = m_NodeRecordList2.Iterate())) { if (NULL != (record_list = node_record->SubNodeList2.Find(node_id))) { if (record_list->Find(entity_id)) rc = TRUE; } } } return rc; } /* * BOOL HasRosterChanged () * * Public Function Description * This routine informs the caller if the roster has changed since the * last time it was reset. */ /* * GCCError ClearNodeRecordFromList () * * Private Function Description * This routine clears out all the application records that exists at * the specified node or below it in the connection hierarchy. * * Formal Parameters * node_id - Node ID of node to clear from Node Record list * * Return Value * GCC_NO_ERROR - No error occured * GCC_INVALID_PARAMETER - The specified node ID is not associated * with any records. * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::ClearNodeRecordFromList( UserID node_id) { GCCError rc = GCC_NO_ERROR; APP_NODE_RECORD *node_record; APP_RECORD *lpAppRecData; //APP_CAP_ITEM *lpAppCapData; CAppRecordList2 *lpAppRecDataList; DebugEntry(CAppRoster::ClearNodeRecordFromList); if (NULL != (node_record = m_NodeRecordList2.Find(node_id))) { // Delete all the app_record_list entries. node_record->AppRecordList.Reset(); while (NULL != (lpAppRecData = node_record->AppRecordList.Iterate())) { DeleteApplicationRecordData (lpAppRecData); } /* ** Delete the ListOfAppCapItemList2 entries. Note that this ** list should not exists for detached nodes if the local applications ** cleanly unenroll with GCC. This list should only exists for ** locally enrolled APEs. We still check here just to make sure. */ if (! node_record->ListOfAppCapItemList2.IsEmpty()) { //CAppCapItemList *lpAppCapDataList; m_fCapabilitiesHaveChanged = TRUE; node_record->ListOfAppCapItemList2.DeleteList(); } // Delete the sub_node list. node_record->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = node_record->SubNodeList2.Iterate())) { // Delete all the app_record_list entries. lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate())) { DeleteApplicationRecordData (lpAppRecData); } delete lpAppRecDataList; } // Delete the collapsed capabilities list. if (! node_record->CollapsedCapList.IsEmpty()) { m_fCapabilitiesHaveChanged = TRUE; node_record->CollapsedCapList.DeleteList(); } // Delete the rogoue wave reference to this roster record. delete node_record; m_NodeRecordList2.Remove(node_id); } else rc = GCC_INVALID_PARAMETER; return rc; } /* * ApplicationRosterError ClearNodeRecordList () * * Private Function Description * This routine complete frees all memory associated with the roster * record list and clears the list of all its entries. * * Formal Parameters * none * * Return Value * none * * Side Effects * none * * Caveats * Currently this routine does not handle standard identifiers. */ void CAppRoster::ClearNodeRecordList(void) { APP_NODE_RECORD *lpAppNodeRec; APP_RECORD *lpAppRecData; CAppRecordList2 *lpAppRecDataList; //APP_CAP_ITEM *lpAppCapData; //CAppCapItemList *lpAppCapDataList; DebugEntry(CAppRoster::ClearNodeRecordList); // Delete all the records in the application roster. m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate())) { // First delete all the app records at this node. lpAppNodeRec->AppRecordList.Reset(); while (NULL != (lpAppRecData = lpAppNodeRec->AppRecordList.Iterate())) { DeleteApplicationRecordData(lpAppRecData); } // Next delete all the sub node record list. lpAppNodeRec->SubNodeList2.Reset(); while (NULL != (lpAppRecDataList = lpAppNodeRec->SubNodeList2.Iterate())) { lpAppRecDataList->Reset(); while (NULL != (lpAppRecData = lpAppRecDataList->Iterate())) { DeleteApplicationRecordData(lpAppRecData); } // Delete the rogue wave list holding the lists of sub nodes. delete lpAppRecDataList; } // Delete the collapsed capabilities list. lpAppNodeRec->CollapsedCapList.DeleteList(); // Delete the list of capabilities list. lpAppNodeRec->ListOfAppCapItemList2.DeleteList(); // Now delete the node record delete lpAppNodeRec; } m_NodeRecordList2.Clear(); } /* * GCCError MakeCollapsedCapabilitiesList () * * Private Function Description * This routine is responsible for applying the T.124 capabilities * rules to create the collapsed capabilities list at this node. * It iterates through all the capabilities at this node to create this * collapsed list. * * Formal Parameters * none * * Return Value * GCC_NO_ERROR - On success * GCC_ALLOCATION_FAILURE - On resource error * * Side Effects * none * * Caveats * Currently this routine does not handle standard identifiers. */ GCCError CAppRoster::MakeCollapsedCapabilitiesList(void) { GCCError rc = GCC_NO_ERROR; APP_CAP_ITEM *lpAppCapData; APP_NODE_RECORD *lpAppNodeRec; CAppCapItemList *lpAppCapDataList; DebugEntry(CAppRoster::MakeCollapsedCapabilitiesList); // First clear out the old capabilities list. m_CollapsedCapListForAllNodes.DeleteList(); /* ** We now iterate through the capabilities at each node to create the ** new capabilities list. Note that we have to check for the collapsed ** capabilities list at each node as well as the list of capabilities list ** that represents all the different capabilities for each entity at a ** node. Note that in this implementation it is not possible to have both ** a list of capabilities list and a collapsed capabilities list in the ** same roster record. */ m_NodeRecordList2.Reset(); while (NULL != (lpAppNodeRec = m_NodeRecordList2.Iterate())) { /* ** First check the collapsed capabilities list. If entries exists ** then we don't have to worry about the list of list. */ if (! lpAppNodeRec->CollapsedCapList.IsEmpty()) { lpAppNodeRec->CollapsedCapList.Reset(); while (NULL != (lpAppCapData = lpAppNodeRec->CollapsedCapList.Iterate())) { rc = AddCapabilityToCollapsedList(lpAppCapData); if (GCC_NO_ERROR != rc) { goto MyExit; // break; } } } else if (! lpAppNodeRec->ListOfAppCapItemList2.IsEmpty()) { // Here we check the list of capabilities list. lpAppNodeRec->ListOfAppCapItemList2.Reset(); while (NULL != (lpAppCapDataList = lpAppNodeRec->ListOfAppCapItemList2.Iterate())) { lpAppCapDataList->Reset(); while (NULL != (lpAppCapData = lpAppCapDataList->Iterate())) { rc = AddCapabilityToCollapsedList(lpAppCapData); if (GCC_NO_ERROR != rc) { goto MyExit; } } } } } ASSERT(GCC_NO_ERROR == rc); MyExit: return rc; } /* * GCCError AddCapabilityToCollapsedList () * * Private Function Description * This is the routine that performs the rules that allow the capability * to be collapsed into the collapsed list. * * Formal Parameters * new_capability - (i) Add this capability to the collapsed list. * * Return Value * GCC_NO_ERROR - On success * GCC_ALLOCATION_FAILURE - On resource error * * Side Effects * none * * Caveats * none */ GCCError CAppRoster::AddCapabilityToCollapsedList(APP_CAP_ITEM *new_capability) { GCCError rc = GCC_NO_ERROR; APP_CAP_ITEM *pAppCapItem; DebugEntry(CAppRoster::AddCapabilityToCollapsedList); /* ** First determine if the capability already exists in the list. ** We must iterate through the complete list to determine if there ** is a matching capability id. */ m_CollapsedCapListForAllNodes.Reset(); while (NULL != (pAppCapItem = m_CollapsedCapListForAllNodes.Iterate())) { if (*pAppCapItem->pCapID == *new_capability->pCapID) { pAppCapItem->cEntries += new_capability->cEntries; break; } } if (pAppCapItem == NULL) { DBG_SAVE_FILE_LINE pAppCapItem = new APP_CAP_ITEM(new_capability, &rc); if (NULL == pAppCapItem) { return GCC_ALLOCATION_FAILURE; } if (GCC_NO_ERROR != rc) { delete pAppCapItem; return rc; } m_CollapsedCapListForAllNodes.Append(pAppCapItem); } /* ** If the unsigned minimum or unsigned maximum rule is used perform the ** operation here. */ ASSERT(GCC_NO_ERROR == rc); if (new_capability->eCapType == GCC_UNSIGNED_MINIMUM_CAPABILITY) { if (new_capability->nUnsignedMinimum < pAppCapItem->nUnsignedMinimum) { pAppCapItem->nUnsignedMinimum = new_capability->nUnsignedMinimum; } } else if (new_capability->eCapType == GCC_UNSIGNED_MAXIMUM_CAPABILITY) { if (new_capability->nUnsignedMaximum > pAppCapItem->nUnsignedMaximum) { pAppCapItem->nUnsignedMaximum = new_capability->nUnsignedMaximum; } } return rc; } /* * BOOL DoCapabilitiesListMatch () * * Private Function Description * This routine determines if the set of capabilities that were passed in * match the set of internal capabilities associated with the record. * * Formal Parameters * node_id - (i) Node ID of record that contains the * capabilities to check. * entity_id - (i) Entity ID of record that contains the * capbilities to check. * number_of_capabilities - (i) Number of capabilities in list to check. * capabilities_list - (i) "API" capabillities list to check * * Return Value * TRUE - If capabillities list match * FALSE - If capabillities list do NOT match * * Side Effects * none * * Caveats * none */ BOOL CAppRoster::DoCapabilitiesListMatch ( UserID node_id, EntityID entity_id, UINT number_of_capabilities, PGCCApplicationCapability * capabilities_list) { BOOL rc = FALSE; CAppCapItemList *pAppCapItemList; GCCError error_value; APP_NODE_RECORD *node_record; UINT i; CCapIDContainer *capability_id; DebugEntry(CAppRoster::DoCapabilitiesListMatch); if (NULL == (node_record = m_NodeRecordList2.Find(node_id))) return (FALSE); if (NULL == (pAppCapItemList = node_record->ListOfAppCapItemList2.Find(entity_id))) { /* ** If the record shows no capabilities and the number of passed ** in capabilities is zero than we got a match. */ return ((number_of_capabilities == 0) ? TRUE : FALSE); } else if (pAppCapItemList->GetCount() != number_of_capabilities) { return (FALSE); } /* ** If we have gotten this far we must iterate through the entire list to ** see if all the capabilities match. */ for (i = 0; i < number_of_capabilities; i++) { /* ** First we create a temporary ID to compare to the other ** capability IDs. */ DBG_SAVE_FILE_LINE capability_id = new CCapIDContainer(&capabilities_list[i]->capability_id, &error_value); if ((capability_id != NULL) && (error_value == GCC_NO_ERROR)) { APP_CAP_ITEM *lpAppCapData; // Start with the return value equal to FALSE rc = FALSE; /* ** Now iterate through the complate internal capability ** list looking for a matching capability. */ pAppCapItemList->Reset(); while (NULL != (lpAppCapData = pAppCapItemList->Iterate())) { if (*capability_id == *lpAppCapData->pCapID) { if (lpAppCapData->eCapType == capabilities_list[i]->capability_class.eType) { if (capabilities_list[i]->capability_class.eType == GCC_UNSIGNED_MINIMUM_CAPABILITY) { if (capabilities_list[i]->capability_class.nMinOrMax == lpAppCapData->nUnsignedMinimum) { rc = TRUE; } } else if (capabilities_list[i]->capability_class.eType == GCC_UNSIGNED_MAXIMUM_CAPABILITY) { if (capabilities_list[i]->capability_class.nMinOrMax == lpAppCapData->nUnsignedMaximum) { rc = TRUE; } } else rc = TRUE; } break; } } // Delete the capability ID data capability_id->Release(); if (rc == FALSE) break; } else { if (NULL != capability_id) { capability_id->Release(); } break; } } return rc; } void CAppRosterList::DeleteList(void) { CAppRoster *pAppRoster; while (NULL != (pAppRoster = Get())) { pAppRoster->Release(); } } void CListOfAppCapItemList2::DeleteList(void) { CAppCapItemList *pAppCapItemList; while (NULL != (pAppCapItemList = Get())) { pAppCapItemList->DeleteList(); delete pAppCapItemList; } }