#include "precomp.h" DEBUG_FILEZONE(ZONE_T120_GCCNC); /* * conf.cpp * * Copyright (c) 1995 by DataBeam Corporation, Lexington, KY * * Abstract: * This is the imlementation file for the CConf Class. The * conference class is the heart of GCC. It maintains all the * information basses for a single conference including conference and * application rosters as well as registry information. It also * routes, encodes and decodes various PDU's and primitives supported * by GCC. * * FOR A MORE DETAILED EXPLANATION OF THIS CLASS SEE THE INTERFACE FILE. * * Portable: * Yes * * Private Instance Variables * m_JoinRespNamePresentConnHdlList2 * This list keeps up with outstanding joins at an intermediate node. * Enrolled_App_List * This list keeps up with all the enrolled applications. * m_ConnHandleList * This list keeps up with all the child node connection handles. * m_ConnHdlTagNumberList2 * This list keeps up with all the outstanding user ID Tag numbers. * m_EjectedNodeConfirmList * This list keeps up with all the nodes that have been ejected but * have yet to disconnect. Used to disconnect any misbehaving apps. * m_pMcsUserObject * Holds a pointer to the MCSUser object owned by this conference. * m_pszConfNumericName * Holds the numeric conference name. * m_pwszConfTextName * Holds a pointer to a unicode string object that contains the text * conference name. NULL if empty. * m_pszConfModifier * Holds a pointer to the conference modifier. NULL if empty. * m_pszRemoteModifier * Holds a pointer to the remote modifier. Only used in Join Confirms. * m_nConfID * Holds the conference ID associated with this conference object. * m_fConfLocked * Flag that indicates if the conference is locked. * m_fConfListed * Flag that indicates if the conference is listed. * m_fConfConductible * Flag that indicates if the conference is conductible. * m_fClearPassword * Flag that indicates if password in the clear is used. * m_nConvenerNodeID * Holds the MCS user ID of the convener. Zero if convener has left. * m_eTerminationMethod * Holds the enumeration that defines the termination method. * m_pDomainParameters * Holds the domain parameters that are returned in a number * of confirms. * m_nUserIDTagNumber * The tag number that must be included when sending this nodes user ID * back to a connected node. * m_nConvenerUserIDTagNumber * Used to uniquely mark the convener when it is rejoining a * conference. * m_nParentIDTagNumber * Used to uniquely mark the parent user ID for an invited node. * m_eNodeType * Holds the enumerated Node Type for this particular node. * m_hParentConnection * Holds the parent logical connection handle. * m_hConvenerConnection * Holds the convener connection handle. * m_fConfIsEstablished * Flag which is set to TRUE when the confernce is completely * established and ready for enrolls and announces. * m_fConfDisconnectPending * Flag which is set to TRUE when a disconnect has been issued but * the conference is waiting for subordinate nodes to disconnect. * m_fConfTerminatePending * Flag which is set to TRUE when a terminate has been issued but * the conference is waiting for subordinate nodes to disconnect. * m_eConfTerminateReason * Maintains the terminate reason for delivery in the indication. * m_pConfTerminateAlarm * Alarm that is used to force automatic termination when * subordinate nodes will not disconnect. * m_pConfStartupAlarm * Alarm used to hold back the flush of the first roster update * indication until all the APEs that wish to enroll have had time * to enroll. * m_pConductorPrivilegeList * Holds a pointer to the conductor privilege list object. * m_pConductModePrivilegeList * Holds a pointer to the conducted mode privilege list object. * m_pNonConductModePrivilegeList * Holds a pointer to the non-conducted mode privilege list object. * m_pwszConfDescription * Holds a pointer to a unicode string which holds the conference * description. * m_pNetworkAddressList * Holds a pointer to an object that contains all the local network * addresses. * m_pUserDataList * Holds a pointer to a user data object needed to deliver an * asynchronus confirm message. * m_nConfAddRequestTagNumber * This instance variable is used to generate the add request tag that * is returned in an add response. * m_nConfAddResponseTag * This instance variable is used to generate a response tag that is * passed in an add indication and returned in an add response. * m_AddRequestList * List that keeps up with all the outstanding add request tags. * m_AddResponseList * List that keeps up with all the outstanding add response tags. * m_pConfRosterMgr * Pointer to the Conference Roster manager. * m_AppRosterMgrList * List which holds pointers to all ofthe Application Roster managers. * m_pAppRegistry * Pointer to the Application Registry object used by this conference. * m_InviteRequestList * List which keeps up with the info associated with all of the * outstanding invite request. Used for cleanup when the invited * node never responds. * m_nConductorNodeID * The MCS user ID associated with the conducting node. Zero if the * conference is not in conducted mode. * m_nPendingConductorNodeID * Used to keep up with the new conductor node ID when conductorship * is being passed from one node to another. * m_fConductorGrantedPermission * Flag which when TRUE specifies that this node has conductor granted * permission. * m_ConductorTestList * List that is used to keep up with all the command targets that have * issued conductor inquire request. * m_fConductorGiveResponsePending * Flag that states if this node is waiting on a conductor give * response. * m_fConductorAssignRequestPending * Flag that states if this node is waiting to here back from an * assign request. * APE_Enitity_ID * This is a counter used to generate application enityt ids. * * Caveats: * Note that the conference object is now split into two seperate files * to prevent text segment problems. This file contains the constructors * and all the entry points for the Owner Object. * * Author: * blp */ #include "conf.h" #include "gcontrol.h" #include "translat.h" #include "ogcccode.h" #ifdef _DEBUG #define STARTUP_TIMER_DURATION 10000 // Ten second startup time #else #define STARTUP_TIMER_DURATION 2000 // Two second startup time #endif extern MCSDLLInterface *g_pMCSIntf; /* * This is a global variable that has a pointer to the one GCC coder that * is instantiated by the GCC Controller. Most objects know in advance * whether they need to use the MCS or the GCC coder, so, they do not need * this pointer in their constructors. */ extern CGCCCoder *g_GCCCoder; /* * CConf::CConf () * * Public Function Description * When pConfSpecParams != NULL * This is the conference constructor. It is responsible for * initializing all the instance variables used by this class. * It also creates the MCS domain based on the conference id. * Fatal errors are returned from this constructor in the * return value. Note that this constructor is used when the * Conference specification parameters such as termination * method or known in advance of conference creation. This is * the case for a CONVENOR node and a TOP PROVIDER. It is not * used for joining nodes. * * When pConfSpecParams == NULL * This is the conference constructor. It is responsible for * initializing all the instance variables used by this class. * It also creates the MCS domain based on the conference id. * Fatal errors are returned from this constructor in the * return value. Note that this constructor is used by nodes that * do not know the Conference specification parameters such as * termination method in advance of conference creation. This is * the case for joining nodes. */ CConf:: CConf ( PGCCConferenceName pConfName, GCCNumericString pszConfModifier, GCCConfID nConfID, CONF_SPEC_PARAMS *pConfSpecParams, UINT cNetworkAddresses, PGCCNetworkAddress *pLocalNetworkAddress, PGCCError pRetCode ) : CRefCount(MAKE_STAMP_ID('C','o','n','f')), m_RegisteredAppSapList(DESIRED_MAX_APP_SAP_ITEMS), m_EnrolledApeEidList2(DESIRED_MAX_APP_SAP_ITEMS), m_ConnHdlTagNumberList2(DESIRED_MAX_CONN_HANDLES), m_JoinRespNamePresentConnHdlList2(CLIST_DEFAULT_MAX_ITEMS), m_InviteRequestList(CLIST_DEFAULT_MAX_ITEMS), m_ConnHandleList(DESIRED_MAX_CONN_HANDLES), m_EjectedNodeConfirmList(CLIST_DEFAULT_MAX_ITEMS), m_AddRequestList(CLIST_DEFAULT_MAX_ITEMS), m_AddResponseList(CLIST_DEFAULT_MAX_ITEMS), m_NodeVersionList2(CLIST_DEFAULT_MAX_ITEMS), m_cEnrollRequests(0), m_fFirstAppRosterSent(FALSE), m_nConfID(nConfID), m_pMcsUserObject(NULL), m_pDomainParameters(NULL), m_pUserDataList(NULL), m_pszRemoteModifier(NULL), m_pConfRosterMgr(NULL), m_pAppRegistry(NULL), m_nConductorNodeID(0), m_nPendingConductorNodeID(0), m_fConductorGrantedPermission(FALSE), m_fConductorGiveResponsePending(FALSE), m_fConductorAssignRequestPending(FALSE), m_hParentConnection(0), m_hConvenerConnection(0), m_pConfTerminateAlarm(NULL), m_nUserIDTagNumber(0), m_nConfAddRequestTagNumber(0), m_nConfAddResponseTag(0), m_nConvenerNodeID(0), m_nConvenerUserIDTagNumber(0), m_nAPEEntityID(0), m_pwszConfTextName(NULL), m_pszConfModifier(NULL), m_pConductorPrivilegeList(NULL), m_pConductModePrivilegeList(NULL), m_pNonConductModePrivilegeList(NULL), m_pwszConfDescription(NULL), m_pNetworkAddressList(NULL), /* This variable transitions to TRUE when the conference has completely ** stabilized. Once it is set to TRUE applications may enroll with the ** conference. */ m_fConfIsEstablished(FALSE), /* This variable is transitioned to TRUE if the node that is ** disconnected is connected to child nodes. */ m_fConfDisconnectPending(FALSE), /* This variable is transitioned to TRUE if a valid Terminate request ** is processed. */ m_fConfTerminatePending(FALSE), /* This variable is set to TRUE if InitiateTermination is called once */ m_fTerminationInitiated(FALSE), m_fSecure(FALSE), m_fWBEnrolled(FALSE), m_fFTEnrolled(FALSE), m_fChatEnrolled(FALSE) { GCCError rc = GCC_ALLOCATION_FAILURE; DebugEntry(CConf::CConf); // Conference Specification if (NULL != pConfSpecParams) { m_fClearPassword = pConfSpecParams->fClearPassword; m_fConfLocked = pConfSpecParams->fConfLocked; m_fConfListed = pConfSpecParams->fConfListed; m_fConfConductible = pConfSpecParams->fConfConductable; m_eTerminationMethod = pConfSpecParams->eTerminationMethod; } // m_pConfStartupAlarm = NULL; DBG_SAVE_FILE_LINE m_pConfStartupAlarm = new Alarm(STARTUP_TIMER_DURATION); if (NULL == m_pConfStartupAlarm) { ERROR_OUT(("CConf::CConf: Error allocating startup alarm")); goto MyExit; } // Save the numeric conference name. if (NULL != pConfName->numeric_string) { if (NULL == (m_pszConfNumericName = ::My_strdupA(pConfName->numeric_string))) { ERROR_OUT(("CConf::CConf: Error allocating conf numeric name")); goto MyExit; } TRACE_OUT(("CConf::CConf: m_strConfNumericName = %s", m_pszConfNumericName)); } else { m_pszConfNumericName = NULL; if (NULL != pConfSpecParams) { // // LONCHANC: It is an error for top-provider. // ERROR_OUT(("CConf::CConf: Error: Numeric Name not present")); rc = GCC_INVALID_CONFERENCE_NAME; goto MyExit; } // // LONCHANC: It is not an error for joining node. // } // Save the text conference name if one exists if (NULL != pConfName->text_string) { if (NULL == (m_pwszConfTextName = ::My_strdupW(pConfName->text_string))) { ERROR_OUT(("CConf::CConf: Error allocating unicode string")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } } // Save the conference modifier if one exists if (NULL != pszConfModifier) { if (NULL == (m_pszConfModifier = ::My_strdupA(pszConfModifier))) { ERROR_OUT(("CConf::CConf: Error allocating conf modifier")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } else { TRACE_OUT(("CConf::CConf: Conference_Modifier = %s", m_pszConfModifier)); } } // Set up the privilege list as needed if (NULL != pConfSpecParams) { if (NULL != pConfSpecParams->pConductPrivilege) { DBG_SAVE_FILE_LINE m_pConductorPrivilegeList = new PrivilegeListData(pConfSpecParams->pConductPrivilege); if (NULL == m_pConductorPrivilegeList) { ERROR_OUT(("CConf::CConf: Error allocating conductor privilege list")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } } if (NULL != pConfSpecParams->pConductModePrivilege) { DBG_SAVE_FILE_LINE m_pConductModePrivilegeList = new PrivilegeListData(pConfSpecParams->pConductModePrivilege); if (NULL == m_pConductModePrivilegeList) { ERROR_OUT(("CConf::CConf: Error allocating conduct mode privilege list")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } } if (NULL != pConfSpecParams->pNonConductPrivilege) { DBG_SAVE_FILE_LINE m_pNonConductModePrivilegeList = new PrivilegeListData(pConfSpecParams->pNonConductPrivilege); if (NULL == m_pNonConductModePrivilegeList) { ERROR_OUT(("CConf::CConf: Error allocating non-conduct mode privilege list")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } } if (NULL != pConfSpecParams->pwszConfDescriptor) { if (NULL == (m_pwszConfDescription = ::My_strdupW(pConfSpecParams->pwszConfDescriptor))) { ERROR_OUT(("CConf::CConf: Error allocating conf descriptor")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } } } // Save the network address(es) if (0 != cNetworkAddresses) { DBG_SAVE_FILE_LINE m_pNetworkAddressList = new CNetAddrListContainer(cNetworkAddresses, pLocalNetworkAddress, &rc); if (NULL == m_pNetworkAddressList || GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::CConf: Error allocating network address list")); rc = GCC_ALLOCATION_FAILURE; // rc could be a different value goto MyExit; } } ASSERT(GCC_ALLOCATION_FAILURE == rc); /* ** Create the Domain based on the conference name that was ** passed in. */ if (MCS_NO_ERROR != g_pMCSIntf->CreateDomain(&m_nConfID)) { ERROR_OUT(("CConf::CConf: Error creating domain")); rc = GCC_FAILURE_CREATING_DOMAIN; goto MyExit; } rc = GCC_NO_ERROR; MyExit: *pRetCode = rc; DebugExitINT(CConf::CConf, rc); } /* * CConf::~CConf () * * Public Function Description * This is the conference destructor. It is responsible for * deleting the User Attachment and freeing up any outstanding * resources used by the conference class. It also calls * MCS Disconnect Provider to disconnect fron all its connections * including both parent and child connections. In addition, it * unregisters its command target from the controller and application * SAPs and deletes the MCS domain it is associated with. * * Caveats * none */ CConf:: ~CConf ( void ) { ConnectionHandle nConnHdl; //CAppRosterMgr *lpAppRosterMgr; ENROLLED_APE_INFO *lpEnrAPEInfo; //CAppSap *pAppSap; DebugEntry(CConf::~CConf); // Delete the terminate alarm if it exists delete m_pConfTerminateAlarm; // Delete the startup alarm if it exists delete m_pConfStartupAlarm; // Delete Conference Roster Managers if (NULL != m_pConfRosterMgr) { m_pConfRosterMgr->Release(); } // Delete Application Roster Managers m_AppRosterMgrList.DeleteList(); // Delete the application registry if (NULL != m_pAppRegistry) { m_pAppRegistry->Release(); } // Delete the text conference name if it exist delete m_pszConfNumericName; delete m_pwszConfTextName; // Delete the conference modifier if it exist delete m_pszConfModifier; // Delete the remote modifier if it exist delete m_pszRemoteModifier; /* ** The privilege list are not directly deleted here instead Free is called ** to prevent the list from being deleted in the case where it has been ** locked outside the conference object. */ // Delete all the privilege list (Free is needed incase the list delete m_pConductorPrivilegeList; delete m_pConductModePrivilegeList; delete m_pNonConductModePrivilegeList; // Delete the conference descriptor delete m_pwszConfDescription; // Delete the network address list if (NULL != m_pNetworkAddressList) { m_pNetworkAddressList->Release(); } if (NULL != m_pUserDataList) { m_pUserDataList->Release(); } // Delete the Domain Parameters if they exist delete m_pDomainParameters; // Delete the User Attachment object if they exist if (NULL != m_pMcsUserObject) { m_pMcsUserObject->Release(); } // Disconnect from the MCS parent connection if (m_hParentConnection != NULL) { g_pMCSIntf->DisconnectProviderRequest(m_hParentConnection); } // Disconnect from all MCS child connections m_ConnHandleList.Reset(); while (0 != (nConnHdl = m_ConnHandleList.Iterate())) { g_pMCSIntf->DisconnectProviderRequest(nConnHdl); } // Delete the MCS domain g_pMCSIntf->DeleteDomain(&m_nConfID); // Cleanup up the m_EnrolledApeEidList2 m_EnrolledApeEidList2.Reset(); while (NULL != (lpEnrAPEInfo = m_EnrolledApeEidList2.Iterate())) { if (NULL != lpEnrAPEInfo->session_key) { lpEnrAPEInfo->session_key->Release(); } delete lpEnrAPEInfo; } DebugExitVOID(CConf::~CConf); } /* ** Non-CommandTarget Calls. Initiated from the Owner Object. Note that ** all calls received from the owner object are preceeded by GCC. */ /* * CConf::ConfCreateRequest() * * Public Function Description * This routine is called from the owner object when a * ConferenceCreateRequest primitive needs to be processed. * If the calling address equals the called address then an * empty conference is created at this node (this node will * then be both the convenor and the top provider). * * Caveats * All errors should be handled directly by the calling application. * This includes deletion of the conference object. */ GCCError CConf:: ConfCreateRequest ( TransportAddress calling_address, TransportAddress called_address, BOOL fSecure, CPassword *convener_password, CPassword *password, LPWSTR pwszCallerID, PDomainParameters domain_parameters, CUserDataListContainer *user_data_list, PConnectionHandle connection_handle ) { GCCError rc = GCC_ALLOCATION_FAILURE; ConnectGCCPDU connect_pdu; LPBYTE encoded_pdu; UINT encoded_pdu_length; MCSError mcs_error; DebugEntry(CConf::ConfCreateRequest); /* ** First make a copy of the new domain parameters if they exists. These ** will be copied over when the connect provider confirm comes in. */ if (NULL != domain_parameters) { DBG_SAVE_FILE_LINE m_pDomainParameters = new DomainParameters; if (NULL == m_pDomainParameters) { ERROR_OUT(("CConf::ConfCreateRequest: can't create DomainParameters")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } // structure-wide shallow copy *m_pDomainParameters = *domain_parameters; } /* ** If the called address equals NULL this node will be both the Top ** Provider and the Convener. In this case there is no need to send out the ** ConfCreateRq PDU. Instead we go ahead and create the User Object. If the ** conference is created with someone else, wait until the response is ** returned before creating the User Object. */ if (NULL != called_address) { // Set up the node type m_eNodeType = CONVENER_NODE; /* ** Create the ConferenceCreateRequest PDU here. */ connect_pdu.choice = CONFERENCE_CREATE_REQUEST_CHOSEN; connect_pdu.u.conference_create_request.bit_mask = 0; // Encode the conference name connect_pdu.u.conference_create_request.conference_name.bit_mask = 0; // Encode the numeric portion of the name ::lstrcpyA(connect_pdu.u.conference_create_request.conference_name.numeric, m_pszConfNumericName); // Encode the text portion of the conference name if it exists if (NULL != m_pwszConfTextName) { connect_pdu.u.conference_create_request.conference_name.bit_mask |= CONFERENCE_NAME_TEXT_PRESENT; connect_pdu.u.conference_create_request.conference_name.conference_name_text.value = m_pwszConfTextName; connect_pdu.u.conference_create_request.conference_name.conference_name_text.length = ::lstrlenW(m_pwszConfTextName); } // Encode the convener password if one exists. if (NULL != convener_password) { rc = convener_password->GetPasswordPDU( &connect_pdu.u.conference_create_request.ccrq_convener_password); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfCreateRequest: can't get convenor password, rc=%d", rc)); goto MyExit; } connect_pdu.u.conference_create_request.bit_mask |= CCRQ_CONVENER_PASSWORD_PRESENT; } // Encode the convener password if one exists. if (NULL != password) { rc = password->GetPasswordPDU( &connect_pdu.u.conference_create_request.ccrq_password); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfCreateRequest: can't get password, rc=%d", rc)); goto MyExit; } connect_pdu.u.conference_create_request.bit_mask |= CCRQ_PASSWORD_PRESENT; } // Encode the privilege list if (NULL != m_pConductorPrivilegeList) { rc = m_pConductorPrivilegeList->GetPrivilegeListPDU( &connect_pdu.u.conference_create_request.ccrq_conductor_privs); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfCreateRequest: can't get conductor's privileges, rc=%d", rc)); goto MyExit; } connect_pdu.u.conference_create_request.bit_mask |= CCRQ_CONDUCTOR_PRIVS_PRESENT; } if (NULL != m_pConductModePrivilegeList) { rc = m_pConductModePrivilegeList->GetPrivilegeListPDU( &connect_pdu.u.conference_create_request.ccrq_conducted_privs); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfCreateRequest: can't get conduct mode privileges, rc=%d", rc)); goto MyExit; } connect_pdu.u.conference_create_request.bit_mask |= CCRQ_CONDUCTED_PRIVS_PRESENT; } if (NULL != m_pNonConductModePrivilegeList) { rc = m_pNonConductModePrivilegeList->GetPrivilegeListPDU( &connect_pdu.u.conference_create_request.ccrq_non_conducted_privs); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfCreateRequest: can't get non-conduct mode privileges, rc=%d", rc)); goto MyExit; } connect_pdu.u.conference_create_request.bit_mask |= CCRQ_NON_CONDUCTED_PRIVS_PRESENT; } // Encode the conference descriptor if (NULL != m_pwszConfDescription) { connect_pdu.u.conference_create_request.bit_mask |= CCRQ_DESCRIPTION_PRESENT; connect_pdu.u.conference_create_request.ccrq_description.length = ::lstrlenW(m_pwszConfDescription); connect_pdu.u.conference_create_request.ccrq_description.value = m_pwszConfDescription; } // Encode the caller identifier if on exists. if (NULL != pwszCallerID) { connect_pdu.u.conference_create_request.bit_mask |= CCRQ_CALLER_ID_PRESENT; connect_pdu.u.conference_create_request.ccrq_caller_id.length = ::lstrlenW(pwszCallerID); connect_pdu.u.conference_create_request.ccrq_caller_id.value = pwszCallerID; } // Encode the user data if any exists if (NULL != user_data_list) { rc = user_data_list->GetUserDataPDU( &connect_pdu.u.conference_create_request.ccrq_user_data); if (GCC_NO_ERROR != NULL) { ERROR_OUT(("CConf::ConfCreateRequest: can't get user data, rc=%d", rc)); goto MyExit; } connect_pdu.u.conference_create_request.bit_mask |= CCRQ_USER_DATA_PRESENT; } connect_pdu.u.conference_create_request.conference_is_locked = (ASN1bool_t)m_fConfLocked; connect_pdu.u.conference_create_request.conference_is_listed = (ASN1bool_t)m_fConfListed; connect_pdu.u.conference_create_request.conference_is_conductible = (ASN1bool_t)m_fConfConductible; connect_pdu.u.conference_create_request.termination_method = (TerminationMethod)m_eTerminationMethod; if (! g_GCCCoder->Encode((LPVOID) &connect_pdu, CONNECT_GCC_PDU, PACKED_ENCODING_RULES, &encoded_pdu, &encoded_pdu_length)) { ERROR_OUT(("CConf::ConfCreateRequest: can't encode")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } mcs_error = g_pMCSIntf->ConnectProviderRequest ( &m_nConfID, // calling domain selector &m_nConfID, // called domain selector calling_address, called_address, fSecure, TRUE, // Upward connection encoded_pdu, encoded_pdu_length, &m_hParentConnection, m_pDomainParameters, this); g_GCCCoder->FreeEncoded(encoded_pdu); *connection_handle = m_hParentConnection; if (MCS_NO_ERROR != mcs_error) { ERROR_OUT(("CConf::ConfCreateRequest: ConnectProviderRequest call failed, rc=%d", mcs_error)); /* ** DataBeam's current implementation of MCS returns ** MCS_INVALID_PARAMETER when something other than ** the transport prefix is wrong with the specified ** transport address. */ rc = (mcs_error == MCS_INVALID_PARAMETER) ? GCC_INVALID_TRANSPORT_ADDRESS : g_pMCSIntf->TranslateMCSIFErrorToGCCError(mcs_error); goto MyExit; } // Free the privilege list packed into structures for encoding if (connect_pdu.u.conference_create_request.bit_mask & CCRQ_CONDUCTOR_PRIVS_PRESENT) { m_pConductorPrivilegeList->FreePrivilegeListPDU( connect_pdu.u.conference_create_request.ccrq_conductor_privs); } if (connect_pdu.u.conference_create_request.bit_mask & CCRQ_CONDUCTED_PRIVS_PRESENT) { m_pConductModePrivilegeList->FreePrivilegeListPDU( connect_pdu.u.conference_create_request.ccrq_conducted_privs); } if (connect_pdu.u.conference_create_request.bit_mask & CCRQ_NON_CONDUCTED_PRIVS_PRESENT) { m_pNonConductModePrivilegeList->FreePrivilegeListPDU( connect_pdu.u.conference_create_request.ccrq_non_conducted_privs); } } else { *connection_handle = NULL; m_eNodeType = TOP_PROVIDER_AND_CONVENER_NODE; DBG_SAVE_FILE_LINE m_pMcsUserObject = new MCSUser(this, 0, 0, &rc); if (NULL == m_pMcsUserObject || GCC_NO_ERROR != rc) { ERROR_OUT(("CConf: ConfCreateRequest: can't create mcs user object, rc=%d", rc)); if (NULL != m_pMcsUserObject) { m_pMcsUserObject->Release(); m_pMcsUserObject = NULL; } else { rc = GCC_ALLOCATION_FAILURE; // rc may be a different value } goto MyExit; } } m_fSecure = fSecure; rc = GCC_NO_ERROR; MyExit: if (GCC_NO_ERROR != rc) { if (NULL != domain_parameters) { delete m_pDomainParameters; m_pDomainParameters = NULL; } } DebugExitINT(CConf::ConferenceCreateRequest, rc); return rc; } /* * CConf::ConfCreateResponse () * * Public Function Description * This routine is called from the owner object when a * ConferenceCreateResponse primitive needs to be processed. * Note that this should only be called when the result of * the response is success. Only the top provider receives the * conference create response. * * Caveats * All errors should be handled directly by the calling application. * This includes deletion of the conference object and notification * to the node controller that the conference was abnormally * terminated. */ GCCError CConf:: ConfCreateResponse ( ConnectionHandle connection_handle, PDomainParameters domain_parameters, CUserDataListContainer *user_data_list ) { GCCError rc = GCC_ALLOCATION_FAILURE; DebugEntry(CConf::ConfCreateResponse); // Conference Create Responses can only be received at the top provider m_eNodeType = TOP_PROVIDER_NODE; /* ** First make a copy of the new domain parameters if they exists. We do ** this here so that they can be passed in when we perform the Connect ** Provider Response. */ if (domain_parameters != NULL) { DBG_SAVE_FILE_LINE m_pDomainParameters = new DomainParameters; if (NULL == m_pDomainParameters) { ERROR_OUT(("CConf::ConfCreateResponse: can't create domain parameters")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } // structure-wide shallow copy *m_pDomainParameters = *domain_parameters; } // Store the child connection in the list of connection handles ASSERT(0 != connection_handle); m_ConnHandleList.Append(connection_handle); /* ** The convener connection handle is stored seperately so that ** the Connect Provider response can be sent on the right ** connection. This may be overkill but I was a little concerned ** about pulling this out of the list even though this should be ** the only entry in the list when it's time to send the response. */ m_hConvenerConnection = connection_handle; if (user_data_list != NULL) { /* ** Since we must wait until the user attachment is fully ** established before we send the response we must save the user ** data list in a temporary container. */ m_pUserDataList = user_data_list; // Lock the user data in memory m_pUserDataList->LockUserDataList(); } /* ** Now create the user attachment object and wait for the confirm ** which occurs after all the proper channels are joined. The ** user object will determine the top provider ID. When the user ** create confirm is received the response PDU will be sent out ** in the Connect Provider Response. */ DBG_SAVE_FILE_LINE m_pMcsUserObject = new MCSUser(this, 0, 0, &rc); if (NULL == m_pMcsUserObject || GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfCreateResponse: can't create mcs user object, rc=%d", rc)); if (NULL != m_pMcsUserObject) { m_pMcsUserObject->Release(); m_pMcsUserObject = NULL; } else { rc = GCC_ALLOCATION_FAILURE; // rc may be a different value } goto MyExit; } rc = GCC_NO_ERROR; MyExit: if (GCC_NO_ERROR != rc) { if (NULL != domain_parameters) { delete m_pDomainParameters; m_pDomainParameters = NULL; } } DebugExitINT(CConf::ConferenceCreateResponse, rc); return rc; } /* * CConf::ConfJoinRequest() * * Public Function Description * This routine is called from the owner object when a * ConferenceJoinRequest primitive received from the node controller needs * to be processed. This routine sends a JoinRequest PDU to its parent * node through an MCS Connect Provider Request. * * Caveats * All errors should be handled directly by the calling application. * This includes deletion of the conference object. */ GCCError CConf:: ConfJoinRequest ( GCCNumericString called_node_modifier, CPassword *convener_password, CPassword *password_challenge, LPWSTR pwszCallerID, TransportAddress calling_address, TransportAddress called_address, BOOL fSecure, PDomainParameters domain_parameters, CUserDataListContainer *user_data_list, PConnectionHandle connection_handle ) { GCCError rc = GCC_ALLOCATION_FAILURE; LPBYTE encoded_pdu; UINT encoded_pdu_length; MCSError mcs_error; ConnectGCCPDU connect_pdu; DebugEntry(CConf::ConfJoinRequest); ASSERT(FALSE == m_fSecure); m_fSecure = fSecure; /* ** First make a copy of the new domain parameters if they exists. These ** will be copied over when the connect provider confirm comes in. */ if (domain_parameters != NULL) { DBG_SAVE_FILE_LINE m_pDomainParameters = new DomainParameters; if (NULL == m_pDomainParameters) { ERROR_OUT(("CConf::ConfJoinRequest: can't create domain parameters")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } *m_pDomainParameters = *domain_parameters; } m_eNodeType = (NULL == convener_password) ? // Node type must be joined when receiving this request JOINED_NODE : // Node type must be joined convener when receiving this request JOINED_CONVENER_NODE; // Create the ConferenceJoinRequest PDU here. connect_pdu.choice = CONNECT_JOIN_REQUEST_CHOSEN; connect_pdu.u.connect_join_request.bit_mask = CONFERENCE_NAME_PRESENT; if (NULL != m_pszConfNumericName && '\0' != *m_pszConfNumericName) { // Send the numeric portion of the conference name connect_pdu.u.connect_join_request.conference_name.choice = NAME_SELECTOR_NUMERIC_CHOSEN; ::lstrcpyA(connect_pdu.u.connect_join_request.conference_name.u.name_selector_numeric, m_pszConfNumericName); } else { // Send the text portion of the conference name connect_pdu.u.connect_join_request.conference_name.choice = NAME_SELECTOR_TEXT_CHOSEN; connect_pdu.u.connect_join_request.conference_name.u.name_selector_text.length = ::lstrlenW(m_pwszConfTextName); connect_pdu.u.connect_join_request.conference_name.u.name_selector_text.value = m_pwszConfTextName; } // Fill in the remote node modifier if one exists if (NULL != called_node_modifier) { // Save the remote modifier so that it can be returned in the confirm if (NULL == (m_pszRemoteModifier = ::My_strdupA(called_node_modifier))) { ERROR_OUT(("CConf::ConfJoinRequest: can't create remote modifier")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } connect_pdu.u.connect_join_request.bit_mask |= CJRQ_CONFERENCE_MODIFIER_PRESENT; ::lstrcpyA(connect_pdu.u.connect_join_request.cjrq_conference_modifier, (LPCSTR) called_node_modifier); } // Fill in the convener password selector. if (NULL != convener_password) { rc = convener_password->GetPasswordSelectorPDU( &connect_pdu.u.connect_join_request.cjrq_convener_password); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinRequest: can't get password selector, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_request.bit_mask |= CJRQ_CONVENER_PASSWORD_PRESENT; } // Fill in the password challenge if (NULL != password_challenge) { rc = password_challenge->GetPasswordChallengeResponsePDU( &connect_pdu.u.connect_join_request.cjrq_password); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinRequest: can't get password challenge response, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_request.bit_mask |=CJRQ_PASSWORD_PRESENT; } // Fill in the caller identifier if one exists if (NULL != pwszCallerID) { connect_pdu.u.connect_join_request.bit_mask |= CJRQ_CALLER_ID_PRESENT; connect_pdu.u.connect_join_request.cjrq_caller_id.value = pwszCallerID; connect_pdu.u.connect_join_request.cjrq_caller_id.length = ::lstrlenW(pwszCallerID); } // Fill in the user data if it exists if (NULL != user_data_list) { rc = user_data_list->GetUserDataPDU( &connect_pdu.u.connect_join_request.cjrq_user_data); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinRequest: can't get user data, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_request.bit_mask |= CJRQ_USER_DATA_PRESENT; } if (! g_GCCCoder->Encode((LPVOID) &connect_pdu, CONNECT_GCC_PDU, PACKED_ENCODING_RULES, &encoded_pdu, &encoded_pdu_length)) { ERROR_OUT(("CConf::ConfJoinRequest: can't encode")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } /* ** Note that the TransportStrings are casted twice here. It ** must be done this way to satisfy the compiler. Sorry about ** not adhearing to coding standards. */ mcs_error = g_pMCSIntf->ConnectProviderRequest( &m_nConfID, // calling domain selector &m_nConfID, // called domain selector calling_address, called_address, m_fSecure, TRUE, // Upward connection encoded_pdu, encoded_pdu_length, &m_hParentConnection, m_pDomainParameters, this); g_GCCCoder->FreeEncoded(encoded_pdu); *connection_handle = m_hParentConnection; if (MCS_NO_ERROR != mcs_error) { ERROR_OUT(("CConf::ConfJoinRequest: can't connect provider request, rc=%d", mcs_error)); /* ** DataBeam's current implementation of MCS returns ** MCS_INVALID_PARAMETER when something other than ** the transport prefix is wrong with the specified ** transport address. */ rc = (mcs_error == MCS_INVALID_PARAMETER) ? GCC_INVALID_TRANSPORT_ADDRESS : g_pMCSIntf->TranslateMCSIFErrorToGCCError(mcs_error); goto MyExit; } rc = GCC_NO_ERROR; MyExit: if (GCC_NO_ERROR != rc) { if (NULL != domain_parameters) { delete m_pDomainParameters; m_pDomainParameters = NULL; } if (NULL != called_node_modifier) { delete m_pszRemoteModifier; m_pszRemoteModifier = NULL; } } DebugExitINT(CConf:ConferenceJoinRequest, rc); return rc; } /* * CConf::ForwardConfJoinRequest () * * Public Function Description * This routine is called from the owner object when a conference join * request is received for a conference that is at a node that is not the * Top Provider. This routine forwards the request on up to the Top * Provider. * * Caveats * This routine should never be called if this node is the Top Provider. */ GCCError CConf:: ForwardConfJoinRequest ( CPassword *convener_password, CPassword *password_challange, LPWSTR pwszCallerID, CUserDataListContainer *user_data_list, BOOL numeric_name_present, ConnectionHandle connection_handle ) { GCCError rc; DebugEntry(CConf::ForwardConfJoinRequest); /* ** If the node is the top provider we will go ahead and send the ** join indication to the node controller, otherwise we will pass ** the request on up to the top provider. */ if (IsConfTopProvider()) { WARNING_OUT(("CConf::GCCConferenceJoinIndication: not top provider")); rc = GCC_INVALID_CONFERENCE; goto MyExit; } /* ** The connection handle is used as the tag which is sent in the request ** and returned in the response. Note that it is the user objects ** responsiblity to resolve any type conflicts with the tag. */ if (NULL == m_pMcsUserObject) { ERROR_OUT(("CConf::GCCConferenceJoinIndication: invalid mcs user object")); rc = GCC_INVALID_CONFERENCE; goto MyExit; } /* ** This list holds information about the conference name alias that ** must be returned in the join response. When the reponse comes ** back from the top provider, the information can be obtained from ** this list. */ m_JoinRespNamePresentConnHdlList2.Append(connection_handle, numeric_name_present ? TRUE_PTR : FALSE_PTR); // The user object is responsible for encoding this PDU rc = m_pMcsUserObject->ConferenceJoinRequest(convener_password, password_challange, pwszCallerID, user_data_list, connection_handle); MyExit: DebugExitINT(CConf::ForwardConfJoinRequest, rc); return rc; } /* * CConf::ConfJoinIndResponse() * * Public Function Description * This routine is called from the owner object when a * ConferenceJoinResponse primitive is received from the node controller. * It is also called when a ConferenceJoinResponse is received from the * Top Provider. * * Caveats * If the GCC_DOMAIN_PARAMETERS_UNACCEPTABLE error is returned from this * routine, MCS will automatically reject the connection sending a * result to the other side stating the the Domain Parameters were * unacceptable. */ GCCError CConf:: ConfJoinIndResponse ( ConnectionHandle connection_handle, CPassword *password_challenge, CUserDataListContainer *user_data_list, BOOL numeric_name_present, BOOL convener_is_joining, GCCResult result ) { GCCError rc = GCC_NO_ERROR; MCSError mcs_error; LPBYTE encoded_pdu; UINT encoded_pdu_length; ConnectGCCPDU connect_pdu; Result mcs_result; DebugEntry(CConf::ConfJoinIndResponse); // Set up the MCS result for the connect provider response. mcs_result = (result == GCC_RESULT_SUCCESSFUL) ? RESULT_SUCCESSFUL : RESULT_USER_REJECTED; // Encode the PDU connect_pdu.choice = CONNECT_JOIN_RESPONSE_CHOSEN; connect_pdu.u.connect_join_response.bit_mask = CJRS_NODE_ID_PRESENT; if (result == GCC_RESULT_SUCCESSFUL) { // Check to see if it is necessary to send the conference name alias if (numeric_name_present && (m_pwszConfTextName != NULL)) { connect_pdu.u.connect_join_response.bit_mask |= CONFERENCE_NAME_ALIAS_PRESENT; connect_pdu.u.connect_join_response.conference_name_alias.choice = NAME_SELECTOR_TEXT_CHOSEN; connect_pdu.u.connect_join_response.conference_name_alias.u.name_selector_text.value = m_pwszConfTextName; connect_pdu.u.connect_join_response.conference_name_alias.u.name_selector_text.length = ::lstrlenW(m_pwszConfTextName); } else if (! numeric_name_present) { connect_pdu.u.connect_join_response.bit_mask |= CONFERENCE_NAME_ALIAS_PRESENT; connect_pdu.u.connect_join_response.conference_name_alias.choice = NAME_SELECTOR_NUMERIC_CHOSEN; lstrcpy (connect_pdu.u.connect_join_response.conference_name_alias.u.name_selector_numeric, m_pszConfNumericName); } // Get the conductor privilege list if (NULL != m_pConductorPrivilegeList) { rc = m_pConductorPrivilegeList->GetPrivilegeListPDU( &connect_pdu.u.connect_join_response.cjrs_conductor_privs); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinIndResponse: can't get privilege, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_response.bit_mask |= CJRS_CONDUCTOR_PRIVS_PRESENT; } // Get the conducted mode privilege list if (NULL != m_pConductModePrivilegeList) { rc = m_pConductModePrivilegeList->GetPrivilegeListPDU( &connect_pdu.u.connect_join_response.cjrs_conducted_privs); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinIndResponse: can't get conduct mode privilege, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_response.bit_mask |= CJRS_CONDUCTED_PRIVS_PRESENT; } // Get the non conducted mode privilege list if (NULL != m_pNonConductModePrivilegeList) { rc = m_pNonConductModePrivilegeList->GetPrivilegeListPDU( &connect_pdu.u.connect_join_response.cjrs_non_conducted_privs); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinIndResponse: can't get non-conduct mode privilege, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_response.bit_mask |= CJRS_NON_CONDUCTED_PRIVS_PRESENT; } // Get the conference description if (NULL != m_pwszConfDescription) { connect_pdu.u.connect_join_response.cjrs_description.length = ::lstrlenW(m_pwszConfDescription); connect_pdu.u.connect_join_response.cjrs_description.value = m_pwszConfDescription; connect_pdu.u.connect_join_response.bit_mask |= CJRS_DESCRIPTION_PRESENT; } } // Get the password challenge if (NULL != password_challenge) { rc = password_challenge->GetPasswordChallengeResponsePDU ( &connect_pdu.u.connect_join_response.cjrs_password); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinIndResponse: can't get password challenge response, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_response.bit_mask |= CJRS_PASSWORD_PRESENT; } // Get the user data list if (NULL != user_data_list) { rc = user_data_list->GetUserDataPDU(&connect_pdu.u.connect_join_response.cjrs_user_data); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfJoinIndResponse: can't get user data, rc=%d", rc)); goto MyExit; } connect_pdu.u.connect_join_response.bit_mask |= CJRS_USER_DATA_PRESENT; } connect_pdu.u.connect_join_response.tag = GetNewUserIDTag (); /* ** if this is the convener rejoining the conference, we save ** the user id tag so that we can record the convener id when it ** is returned in the user id indication. */ if (convener_is_joining && ((m_eNodeType == TOP_PROVIDER_NODE) || (m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE))) { m_nConvenerUserIDTagNumber = connect_pdu.u.connect_join_response.tag; } connect_pdu.u.connect_join_response.cjrs_node_id = m_pMcsUserObject->GetMyNodeID(); connect_pdu.u.connect_join_response.top_node_id = m_pMcsUserObject->GetTopNodeID(); connect_pdu.u.connect_join_response.clear_password_required = (ASN1bool_t)m_fClearPassword; connect_pdu.u.connect_join_response.conference_is_locked = (ASN1bool_t)m_fConfLocked; connect_pdu.u.connect_join_response.conference_is_listed = (ASN1bool_t)m_fConfListed; connect_pdu.u.connect_join_response.conference_is_conductible = (ASN1bool_t)m_fConfConductible; connect_pdu.u.connect_join_response.termination_method = (TerminationMethod)m_eTerminationMethod; connect_pdu.u.connect_join_response.result = ::TranslateGCCResultToJoinResult(result); if (! g_GCCCoder->Encode((LPVOID) &connect_pdu, CONNECT_GCC_PDU, PACKED_ENCODING_RULES, &encoded_pdu, &encoded_pdu_length)) { ERROR_OUT(("CConf::ConfJoinIndResponse: can't encode")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } mcs_error = g_pMCSIntf->ConnectProviderResponse( connection_handle, &m_nConfID, m_pDomainParameters, mcs_result, encoded_pdu, encoded_pdu_length); g_GCCCoder->FreeEncoded(encoded_pdu); if ((mcs_error == MCS_NO_ERROR) && (result == GCC_RESULT_SUCCESSFUL)) { /* ** Add the connection handle to our list of ** connection handles. */ ASSERT(0 != connection_handle); m_ConnHandleList.Append(connection_handle); /* ** Add the user's tag number to the list of outstanding ** user ids along with its associated connection. */ m_ConnHdlTagNumberList2.Append(connect_pdu.u.connect_join_response.tag, connection_handle); } else { WARNING_OUT(("CConf::ConfJoinIndResponse: ConnectProviderResponse failed, mcs_error=%d, result=%d", mcs_error, result)); rc = g_pMCSIntf->TranslateMCSIFErrorToGCCError(mcs_error); goto MyExit; } // Free up any memory allocated by the conatiners to build the PDU structs if (connect_pdu.u.connect_join_response.bit_mask & CJRS_CONDUCTOR_PRIVS_PRESENT) { m_pConductorPrivilegeList->FreePrivilegeListPDU( connect_pdu.u.connect_join_response.cjrs_conductor_privs); } if (connect_pdu.u.connect_join_response.bit_mask & CJRS_CONDUCTED_PRIVS_PRESENT) { m_pConductModePrivilegeList->FreePrivilegeListPDU( connect_pdu.u.connect_join_response.cjrs_conducted_privs); } if (connect_pdu.u.connect_join_response.bit_mask & CJRS_NON_CONDUCTED_PRIVS_PRESENT) { m_pNonConductModePrivilegeList->FreePrivilegeListPDU( connect_pdu.u.connect_join_response.cjrs_non_conducted_privs); } ASSERT(GCC_NO_ERROR == rc); MyExit: if (GCC_NO_ERROR != rc) { g_pGCCController->FailConfJoinIndResponse(m_nConfID, connection_handle); } g_pGCCController->RemoveConfJoinInfo(connection_handle); DebugExitINT(CConf::ConfJoinIndResponse, rc); return rc; } /* * CConf::ConfInviteResponse() * * Public Function Description * This routine is called from the owner object when a * ConferenceInviteResponse primitive needs to be processed. */ GCCError CConf:: ConfInviteResponse ( UserID parent_user_id, UserID top_user_id, TagNumber tag_number, ConnectionHandle connection_handle, BOOL fSecure, PDomainParameters domain_parameters, CUserDataListContainer *user_data_list ) { GCCError rc = GCC_ALLOCATION_FAILURE; LPBYTE encoded_pdu; UINT encoded_pdu_length; MCSError mcs_error; ConnectGCCPDU connect_pdu; DebugEntry(CConf::ConfInviteResponse); ASSERT(FALSE == m_fSecure); m_fSecure = fSecure; // First make a copy of the new domain parameters if they exists if (domain_parameters != NULL) { DBG_SAVE_FILE_LINE m_pDomainParameters = new DomainParameters; if (NULL == m_pDomainParameters) { ERROR_OUT(("CConf::ConfInviteResponse: can't create domain parameters")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } // structure-wide shallow copy *m_pDomainParameters = *domain_parameters; } m_eNodeType = INVITED_NODE; m_nParentIDTagNumber = tag_number; m_hParentConnection = connection_handle; /* ** First we must send the invite response back to the requester. ** If we've gotten this far it will always be a positive response. */ // Create the ConferenceInviteRequest PDU here. connect_pdu.choice = CONFERENCE_INVITE_RESPONSE_CHOSEN; connect_pdu.u.conference_invite_response.bit_mask = 0; if (user_data_list != NULL) { rc = user_data_list->GetUserDataPDU( &connect_pdu.u.conference_invite_response.cirs_user_data); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfInviteResponse: can't get user data, rc=%d", rc)); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } connect_pdu.u.conference_invite_response.bit_mask |= CIRS_USER_DATA_PRESENT; } connect_pdu.u.conference_invite_response.result = ::TranslateGCCResultToInviteResult(GCC_RESULT_SUCCESSFUL); if (! g_GCCCoder->Encode((LPVOID) &connect_pdu, CONNECT_GCC_PDU, PACKED_ENCODING_RULES, &encoded_pdu, &encoded_pdu_length)) { ERROR_OUT(("CConf::ConfInviteResponse: can't encode")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } /* ** Note that the TransportStrings are casted twice here. It ** must be done this way to satisfy the compiler. Sorry about ** not adhearing to coding standards. */ mcs_error = g_pMCSIntf->ConnectProviderResponse ( connection_handle, &m_nConfID, m_pDomainParameters, RESULT_SUCCESSFUL, encoded_pdu, encoded_pdu_length); g_GCCCoder->FreeEncoded(encoded_pdu); if (MCS_NO_ERROR != mcs_error) { WARNING_OUT(("CConf::ConfInviteResponse: ConnectProviderRequest failed: rc=%d", mcs_error)); rc = g_pMCSIntf->TranslateMCSIFErrorToGCCError(mcs_error); goto MyExit; } /* ** Now create the user attachment object and wait for the ** confirm which occurs after all the proper channels are ** joined. The user object will determine the top provider ID. ** When the user create confirm is received the response PDU ** will be sent out in the Connect Provider Response. */ DBG_SAVE_FILE_LINE m_pMcsUserObject = new MCSUser(this, top_user_id, parent_user_id, &rc); if (NULL == m_pMcsUserObject || GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::ConfInviteResponse: Creation of User Attachment failed, rc=%d", rc)); if (NULL != m_pMcsUserObject) { m_pMcsUserObject->Release(); m_pMcsUserObject = NULL; } else { rc = GCC_ALLOCATION_FAILURE; } goto MyExit; } rc = GCC_NO_ERROR; MyExit: if (GCC_NO_ERROR != rc) { if (NULL != domain_parameters) { delete m_pDomainParameters; m_pDomainParameters = NULL; } } DebugExitINT(CConf::ConfInviteResponse, rc); return rc; } /* * CConf::RegisterAppSap() * * Public Function Description * This routine is called from the owner object whenever an application * SAP becomes a candidate for Enrollment. This will happen whenever * Applications SAPs exists at the same time a conference becomes * established. It will also be called whenever a conference exists * and a new application SAP is created. */ GCCError CConf:: RegisterAppSap ( CAppSap *pAppSap ) { GCCError rc; GCCConferenceName conference_name; GCCNumericString conference_modifier; DebugEntry(CConf::RegisterAppSap); if (m_fConfIsEstablished) { /* ** We first check to see if the application is already registered. ** If so, we do not add it to the list of registered applications ** again. */ if (NULL == m_RegisteredAppSapList.Find(pAppSap)) { // Add the application sap pointer to the registered sap list. pAppSap->AddRef(); m_RegisteredAppSapList.Append(pAppSap); /* ** Get the conference name and modifier to send back in the ** permission to enroll indication. */ GetConferenceNameAndModifier(&conference_name, &conference_modifier); // Inform the application that it can now enroll. pAppSap->PermissionToEnrollIndication(m_nConfID, TRUE); } rc = GCC_NO_ERROR; } else { ERROR_OUT(("CConf::RegisterAppSap: CConf not established")); rc = GCC_CONFERENCE_NOT_ESTABLISHED; } DebugExitINT(CConf::RegisterAppSap, rc); return rc; } /* * CConf::UnRegisterAppSap () * * Public Function Description * This routine is called from the owner object whenever an application * SAP becomes unavailable due to whatever reason. This routine is * responsible for unenrolling any APEs from any rosters that it might have * used this SAP to enroll with. */ GCCError CConf:: UnRegisterAppSap ( CAppSap *pAppSap ) { GCCError rc = GCC_NO_ERROR; GCCConferenceName conference_name; GCCNumericString conference_modifier; DebugEntry(CConf::UnRegisterAppSap); /* ** We first check to see if the application is already registered. ** If so, we do not add it to the list of registered applications ** again. */ if (! m_RegisteredAppSapList.Find(pAppSap)) { TRACE_OUT(("CConf::UnRegisterAppSap: app not registered")); rc = GCC_APPLICATION_NOT_REGISTERED; goto MyExit; } /* ** Get the conference name and modifier to send back in the ** permission to enroll indication. */ GetConferenceNameAndModifier(&conference_name, &conference_modifier); // Inform the application that it can no longer enroll. pAppSap->PermissionToEnrollIndication(m_nConfID, FALSE); /* ** We unenroll the appropriate APE. Note that we will only send roster updates ** if the conference is established. */ RemoveSAPFromAPEList(pAppSap); #if 0 // LONCHANC: UnRegisterAppSap should not affect the roster. // Only UnenrollApp will affect the app roster. if (m_fConfIsEstablished) { /* ** Here we flush any PDU data or messages that might have gotten ** queued up when this SAP we was being unenrolled. ** An error here is considered FATAL in that the conference ** information base at this node is now corrupted therefore we ** terminate the conference. */ rc = AsynchFlushRosterData(); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::UnRegisterAppSap: can't flush roster data, rc=%d", rc)); // // Do not need to initiate termination because we are // either already in termination procedure or // the application is going away. // #if 0 InitiateTermination((rc == GCC_ALLOCATION_FAILURE) ? GCC_REASON_ERROR_LOW_RESOURCES : GCC_REASON_ERROR_TERMINATION, 0); #endif // 0 goto MyExit; } } #endif // 0 // Remove the application sap from list of registered SAPs if (m_RegisteredAppSapList.Remove(pAppSap)) { // // Only when this app sap is still in the list, we then can // release it. It is possible that this app sap has been // unregistered by the app due to permission-to-enroll-ind // we just sent earlier. // pAppSap->Release(); } ASSERT(GCC_NO_ERROR == rc); MyExit: DebugExitINT(CConf::UnRegisterAppSap, rc); return rc; } /* * CConf::AppEnrollRequest() * * Public Function Description * This routine is called from the owner object when an * Application is requesting to enroll with this conference. * * Caveats * Enroll confirms are performed by the application roster manager so * anyplace where the application roster manager isn't informed of the * enroll we must perform the enroll confirm here in this routine. */ GCCError CConf:: AppEnrollRequest ( CAppSap *pAppSap, GCCEnrollRequest *pReq, GCCRequestTag nReqTag ) { GCCError rc = GCC_NO_ERROR; CAppRosterMgr *pAppRosterMgr; CAppRosterMgr *pNewAppRosterMgr = NULL; // a must EntityID eid, new_eid = GCC_INVALID_EID; // a must GCCNodeID nid; GCCAppEnrollConfirm aec; DebugEntry(CConf::AppEnrollRequest); TRACE_OUT_EX(ZONE_T120_APP_ROSTER, ("CConf::AppEnrollRequest: " "enrolled?=%u\r\n", (UINT) pReq->fEnroll)); // If the conference is not established return an error. if (! m_fConfIsEstablished) { WARNING_OUT(("CConf::AppEnrollRequest: CConf not established")); rc = GCC_CONFERENCE_NOT_ESTABLISHED; goto MyExit; } if (! m_RegisteredAppSapList.Find(pAppSap)) { WARNING_OUT(("CConf::AppEnrollRequest: app not registered")); rc = GCC_APPLICATION_NOT_REGISTERED; goto MyExit; } if (pReq->fEnroll) { m_cEnrollRequests++; } else { m_cEnrollRequests--; } TRACE_OUT_EX(ZONE_T120_APP_ROSTER, ("CConf::AppEnrollRequest: cEnroll=%d\r\n", m_cEnrollRequests)); if (m_cEnrollRequests < 0) { // // LONCHANC: It seems to me that the upper layer does unenroll without // enroll first. it happens when someone calls me. // We should change this later and have a way to know whether the app // already enrolled or not. // m_cEnrollRequests = 0; } // get the node id nid = m_pMcsUserObject->GetMyNodeID(); /* ** Is the application enrolling or unenrolling? Here we set up the ** application roster and APE information if enrolling. */ if (pReq->fEnroll) // Appplication is enrolling { TRACE_OUT(("CConf::AppEnrollRequest: Application is Enrolling")); /* ** First determine if this APE has already enrolled with this ** conference. If it hasn't we must generate a new EntityID for ** this APE. */ rc = GetEntityIDFromAPEList(pAppSap, pReq->pSessionKey, &eid); if (rc == GCC_APP_NOT_ENROLLED) { rc = GenerateEntityIDForAPEList(pAppSap, pReq->pSessionKey, &new_eid); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::AppEnrollRequest: can't generate entity id, rc=%d", rc)); goto MyExit; } eid = new_eid; m_pAppRegistry->EnrollAPE(eid, pAppSap); } /* ** This takes care of setting up the application roster manager ** if none exists or it will call the appropriate manager with ** the enroll. */ pAppRosterMgr = GetAppRosterManager(pReq->pSessionKey); if (pAppRosterMgr == NULL) { DBG_SAVE_FILE_LINE pNewAppRosterMgr = new CAppRosterMgr( pReq->pSessionKey, NULL, m_nConfID, m_pMcsUserObject, this, &rc); if (NULL == pNewAppRosterMgr || GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::AppEnrollRequest: can't create app roster mgr, rc=%d", rc)); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } pAppRosterMgr = pNewAppRosterMgr; } /* ** Doing the enroll here ensures that an empty roster ** manager will not get put in to the roster manager ** list if the Enroll Fails. */ rc = pAppRosterMgr->EnrollRequest(pReq, eid, nid, pAppSap); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::AppEnrollRequest: EnrollRequest failed, rc=%d", rc)); goto MyExit; } /* ** If this is a new roster manager we will append it to ** the list of roster managers here if no errors occcured. */ if (pNewAppRosterMgr != NULL) { m_AppRosterMgrList.Append(pNewAppRosterMgr); } /* ** Here we inform the newly enrolled application of the ** conductor status if the conference is conductible. */ if (m_fConfConductible) { if (m_nConductorNodeID != 0) { pAppSap->ConductorAssignIndication(m_nConductorNodeID, m_nConfID); } else { pAppSap->ConductorReleaseIndication(m_nConfID); } } } else // Appplication is unenrolling { TRACE_OUT(("CConf::AppEnrollRequest: Application is UnEnrolling")); if (pReq->pSessionKey != NULL) { rc = GetEntityIDFromAPEList(pAppSap, pReq->pSessionKey, &eid); if (rc != GCC_NO_ERROR) { WARNING_OUT(("CConf::AppEnrollRequest: app not enrolled")); goto MyExit; } pAppRosterMgr = GetAppRosterManager(pReq->pSessionKey); if (NULL == pAppRosterMgr) { WARNING_OUT(("CConf::AppEnrollRequest: app not enrolled")); rc = GCC_APP_NOT_ENROLLED; goto MyExit; } /* ** UnEnroll this APE from the specified session. ** Note that the application roster manager will send ** the enroll confirm. */ pAppRosterMgr->UnEnrollRequest(pReq->pSessionKey, eid); // UnEnroll this APE from the registry m_pAppRegistry->UnEnrollAPE(eid); /* ** Since this APE is no longer enrolled remove it from ** the list of APEs. */ DeleteEnrolledAPE(eid); } else { TRACE_OUT(("CConf::AppEnrollRequest: null session key")); /* ** Since a null session key was passed in we will go ahead ** and unenroll all the APEs associated with this sap. */ RemoveSAPFromAPEList(pAppSap); } } /* ** Here we take care of sending the enroll confirm to the application ** SAP that enrolled. We also flush any roster PDUs and messages that ** might have been queued up during the enrollment process above. Note ** that we only send a successful enroll confirm if no errors occured. */ ASSERT(GCC_NO_ERROR == rc); /* ** First we sent the enroll confirm. It is important to send this ** before the flush so that the node ID and entity ID will be ** received by a top provider node before the roster is delivered ** with the applications record in it. This makes it easier on the ** developer when trying to determine if the enrolled record is in ** the roster when the roster report is received. */ aec.nConfID = m_nConfID; aec.sidMyself = pReq->pSessionKey->session_id; aec.eidMyself = eid; aec.nidMyself = nid; aec.nResult = GCC_RESULT_SUCCESSFUL; aec.nReqTag = nReqTag; pAppSap->AppEnrollConfirm(&aec); /* ** Now we flush all the application roster managers of any PDU data ** that might have been queued up during enrollment. This also ** gives the roster managers a chance to deliver roster update if ** necessary. Note that we build and deliver the high level ** portion of the PDU here. Since only application roster stuff will be ** sent in this pdu we must set the pointer to the conference ** information to NULL so that the encoder wont try to encode it. ** An error here is considered FATAL in that the conference information ** base at this node is now corrupted therefore we terminate the ** conference. Note that we only do the flush here if the start up ** alarm has expired. */ rc = AsynchFlushRosterData(); if (rc != GCC_NO_ERROR) { ERROR_OUT(("CConf::AppEnrollRequest: can't flush roster data, rc=%d", rc)); InitiateTermination((rc == GCC_ALLOCATION_FAILURE) ? GCC_REASON_ERROR_LOW_RESOURCES : GCC_REASON_ERROR_TERMINATION, 0); goto MyExit; } ASSERT(GCC_NO_ERROR == rc); MyExit: if (GCC_NO_ERROR != rc && pReq->fEnroll) { if (NULL != pNewAppRosterMgr) { pNewAppRosterMgr->Release(); } if (new_eid != GCC_INVALID_EID) { // UnEnroll this APE from the registry m_pAppRegistry->UnEnrollAPE(new_eid); DeleteEnrolledAPE(new_eid); } } DebugExitINT(CConf::AppEnrollRequest, rc); return rc; } /* * CConf::DisconnectProviderIndication () * * Public Function Description * This routine is called from the owner object when a * Disconnect Provider is received from the MCS interface. * Since the owner object has no way of knowing which connections * are associated with which conferences it may be possible to * receive a disconnect provider for a connection that is not in * the conferences list. */ GCCError CConf:: DisconnectProviderIndication ( ConnectionHandle connection_handle ) { GCCError rc = GCC_NO_ERROR; GCCNodeID nidDisconnected; DebugEntry(CConf::DisconnectProviderIndication); TRACE_OUT(("CConf::DisconnectProviderIndication: connection_handle = %d", connection_handle)); // reject any pending join ind response ConnectionHandle hConn; while (NULL != m_JoinRespNamePresentConnHdlList2.Get(&hConn)) { if (NULL != g_pGCCController) { g_pGCCController->FailConfJoinIndResponse(0, hConn); } } // First check for the parent connection going down. if (m_hParentConnection == connection_handle) { TRACE_OUT(("CConf::DisconnectProviderIndication: Connection == PARENT")); /* ** If the Parent Connection is broken the conference must be ** terminated since this node no longer has access to the top ** gcc provider. */ m_hParentConnection = NULL; InitiateTermination ( GCC_REASON_PARENT_DISCONNECTED, 0); } else { /* ** Now check to see if this connection is associated with an ejected ** node. */ nidDisconnected = m_pMcsUserObject->GetUserIDFromConnection(connection_handle); if (m_EjectedNodeConfirmList.Remove(nidDisconnected)) { #ifdef JASPER g_pControlSap->ConfEjectUserConfirm(m_nConfID, nidDisconnected, GCC_RESULT_SUCCESSFUL); #endif // JASPER } // If this is the convener set its node id back to 0 if (m_nConvenerNodeID == nidDisconnected) m_nConvenerNodeID = 0; // Inform the User Attachment object that another user disconnected m_pMcsUserObject->UserDisconnectIndication (nidDisconnected); ASSERT(0 != connection_handle); if (m_ConnHandleList.Remove(connection_handle)) { TRACE_OUT(("CConf::DisconnectProviderIndication: Connection = CHILD")); /* ** If there is a disconnect pending we must send the disconnect ** confirm here and terminate the conference. Note that in this ** case, the m_fConfIsEstablished variable was set to FALSE ** when the Disconnect Request was issued (therefore a terminate ** indication will not be sent). */ if (m_ConnHandleList.IsEmpty() && m_fConfDisconnectPending) { TRACE_OUT(("CConf::DisconnectProviderIndication: conf disconnect confirm")); /* ** First inform the control SAP that this node has successfuly ** disconnected. */ g_pControlSap->ConfDisconnectConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL); // Tell the owner object to terminate this conference InitiateTermination(GCC_REASON_NORMAL_TERMINATION, m_pMcsUserObject->GetMyNodeID()); } else if (m_ConnHandleList.IsEmpty() && m_fConfTerminatePending) { TRACE_OUT(("CConf::DisconnectProviderIndication: Terminate Request is Completed")); InitiateTermination(m_eConfTerminateReason, m_pMcsUserObject->GetMyNodeID()); } else if (m_ConnHandleList.IsEmpty() && ((m_eNodeType == TOP_PROVIDER_NODE) || (m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE)) && (m_eTerminationMethod == GCC_AUTOMATIC_TERMINATION_METHOD)) { /* ** If nothing is left in our connection list and we are the Top ** Provider and automatic termination is enabled then terminate ** the conference. */ TRACE_OUT(("CConf::DisconnectProviderIndication: AUTOMATIC_TERMINATION")); InitiateTermination( GCC_REASON_NORMAL_TERMINATION, 0); } } else { rc = GCC_INVALID_PARAMETER; } } DebugExitINT(CConf::DisconnectProviderIndication, rc); return rc; } /* * void ConfRosterInquireRequest() * * Public Function Description * This function is used to obtain a conference roster. The conference * roster is delivered to the requesting command target in a Conference * Roster inquire confirm. */ GCCError CConf:: ConfRosterInquireRequest ( CBaseSap *pSap, GCCAppSapMsgEx **ppMsgEx ) { GCCError rc = GCC_NO_ERROR; CConfRoster *conference_roster; LPWSTR pwszConfDescription = NULL; GCCConferenceName conference_name; GCCNumericString conference_modifier; DebugEntry(CConf::ConfRosterInquireRequest); if (m_fConfIsEstablished) { /* ** We use the actual conference roster here to build the roster ** inquire confirm message. Note that the SAP should NOT free this ** roster. */ conference_roster = m_pConfRosterMgr->GetConferenceRosterPointer(); if (conference_roster != NULL) { GetConferenceNameAndModifier(&conference_name, &conference_modifier); if (m_pwszConfDescription != NULL) { pwszConfDescription= m_pwszConfDescription; } pSap->ConfRosterInquireConfirm(m_nConfID, &conference_name, conference_modifier, pwszConfDescription, conference_roster, GCC_RESULT_SUCCESSFUL, ppMsgEx); } else { ERROR_OUT(("CConf::ConfRosterInquireRequest: conf roster does not exist")); rc = GCC_ALLOCATION_FAILURE; } } else { ERROR_OUT(("CConf::ConfRosterInquireRequest: conference not established")); rc = GCC_CONFERENCE_NOT_ESTABLISHED; } DebugExitINT(CConf:ConfRosterInquireRequest, rc); return rc; } /* * CConf::AppRosterInquireRequest() * * Public Function Description * This function is used to obtain a list of application rosters. This * list is delivered to the requesting SAP through an Application Roster * inquire confirm message. */ GCCError CConf:: AppRosterInquireRequest ( PGCCSessionKey session_key, CAppRosterMsg **ppAppRosterMsgOut ) { GCCError rc; BOOL roster_manager_found = FALSE; CAppRosterMsg *roster_message = NULL; CAppRosterMgr *lpAppRosterMgr; DebugEntry(CConf::AppRosterInquireRequest); if (m_AppRosterMgrList.IsEmpty()) { WARNING_OUT(("CConf::AppRosterInquireRequest: app roster mgr is empty")); rc = GCC_NO_SUCH_APPLICATION; goto MyExit; } // First allocate the application roster message DBG_SAVE_FILE_LINE roster_message = new CAppRosterMsg(); if (NULL == roster_message) { ERROR_OUT(("CConf::AppRosterInquireRequest: can't create app roster msg")); rc = GCC_ALLOCATION_FAILURE; goto MyExit; } rc = GCC_NO_ERROR; m_AppRosterMgrList.Reset(); if (session_key != NULL) { while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate())) { roster_manager_found = lpAppRosterMgr->IsThisYourSessionKey (session_key); if (roster_manager_found) { rc = lpAppRosterMgr->ApplicationRosterInquire(session_key, roster_message); break; } } } else { while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate())) { rc = lpAppRosterMgr->ApplicationRosterInquire (NULL, roster_message); if (rc != GCC_NO_ERROR) break; } } MyExit: if (GCC_NO_ERROR == rc) { *ppAppRosterMsgOut = roster_message; // do not call roster_message->Release() because the sap will call it as needed } else { if (NULL != roster_message) { roster_message->Release(); } } DebugExitINT(CConf::AppRosterInquireRequest, rc); return rc; } /* * CConf::FlushOutgoingPDU() * * Public Function Description * This is the heartbeat for the conference object. The conference * is responsible for giving the User Attachment object its * heartbeat. * Return value: * TRUE, if there remain un-processed msgs in the CConf message queue * FALSE, if all the msgs in the conference msg queue were processed. */ BOOL CConf:: FlushOutgoingPDU ( void ) { //GCCError error_value; BOOL fFlushMoreData = FALSE; if (m_fConfTerminatePending && m_pConfTerminateAlarm != NULL) { if (m_pConfTerminateAlarm->IsExpired()) { delete m_pConfTerminateAlarm; m_pConfTerminateAlarm = NULL; InitiateTermination(m_eConfTerminateReason, m_pMcsUserObject->GetTopNodeID()); } else { fFlushMoreData = TRUE; } } if (m_pMcsUserObject != NULL) { m_pMcsUserObject->CheckEjectedNodeAlarms(); fFlushMoreData |= m_pMcsUserObject->FlushOutgoingPDU(); } return fFlushMoreData; } /* * BOOL IsConfEstablished () * * Public Function Description * The conference is established when it is ready to be enrolled * with. No application permission to enrolls should be sent until * this routine returns TRUE. */ /* * BOOL IsConfTopProvider () * * Public Function Description * Function informs whether this node is the Top Provider of the * conference. */ /* * GCCNodeID GetTopProvider () * * Public Function Description * Function returns the GCCNodeID of the Top Provider of the conference. */ /* * BOOL DoesConvenerExists () * * Public Function Description * This function informs whether or not the convener is still joined to * this conference. */ /* * LPSTR GetNumericConfName() * * Public Function Description * Returns a pointer to the numeric portion of the conference name. Used * for comparisons. */ /* * LPWSTR GetTextConfName() * * Public Function Description * Returns a pointer to the text portion of the conference name. Used for * comparisons. */ /* * LPSTR GetConfModifier() * * Public Function Description * Returns a pointer to the conference name modifier. */ /* * LPWSTR GetConfDescription() * * Public Function Description * Returns a pointer to the conference description. */ /* * CNetAddrListContainer *GetNetworkAddressList() * * Public Function Description * Returns a pointer to the network address list. */ /* * GCCConfID GetConfID() * * Public Function Description * Returns the conference ID. */ /* * BOOL IsConfListed() * * Public Function Description * Returns the listed flag. */ /* * BOOL IsConfPasswordInTheClear() * * Public Function Description * Returns the password protected flag. */ /* * BOOL IsConfLocked() * * Public Function Description * Returns the locked flag. */ /* ** These routines operate on the m_EnrolledApeEidList2. */ /* * CConf::GetEntityIDFromAPEList () * * Private Function Description * This routine determines what the entity id is for the specified APE * (note that an APE is defined by its SAP handle and the session key * of the session it is enrolled in). If there is no entity ID associated * with this APE an error is returned. * * Formal Parameters: * hSap - (i) SAP handle associated with the entity ID being * searched for. * session_key - (i) Session key associated with the entity ID being * searched for. * entity_id - (o) The found entity ID is returned here (or zero if * none is found). * * Return Value * GCC_NO_ERROR - No error occured. * GCC_ALLOCATION_FAILURE - A resource error occured allocating session * key data. * GCC_APP_NOT_ENROLLED - Entity ID was not found. * * Side Effects * None. * * Caveats * None. */ GCCError CConf:: GetEntityIDFromAPEList ( CAppSap *pAppSap, PGCCSessionKey session_key, GCCEntityID *pEid ) { GCCError rc = GCC_ALLOCATION_FAILURE; CSessKeyContainer *pSessKey; *pEid = GCC_INVALID_EID; DBG_SAVE_FILE_LINE pSessKey = new CSessKeyContainer(session_key, &rc); if (pSessKey != NULL && rc == GCC_NO_ERROR) { GCCEntityID eid; ENROLLED_APE_INFO *lpEnrAPEInfo; m_EnrolledApeEidList2.Reset(); while (NULL != (lpEnrAPEInfo = m_EnrolledApeEidList2.Iterate(&eid))) { if (pAppSap == lpEnrAPEInfo->pAppSap && *pSessKey == *lpEnrAPEInfo->session_key) { *pEid = eid; break; } } if (*pEid == GCC_INVALID_EID) { TRACE_OUT(("CConf::GetEntityIDFromAPEList: App NOT Enrolled")); rc = GCC_APP_NOT_ENROLLED; } } // Free up the temporary session key. if (NULL != pSessKey) { pSessKey->Release(); } return rc; } /* * CConf::GenerateEntityIDForAPEList () * * Private Function Description * This function is responsible for generating a unqiue entity ID for * the specified APE. * * Formal Parameters: * hSap - (i) SAP handle associated with the entity ID being * generated. * session_key - (i) Session key associated with the entity ID being * generated. * entity_id - (o) The generated entity ID is returned here. * * Return Value * GCC_NO_ERROR - No error occured. * GCC_ALLOCATION_FAILURE - A resource error occured. * * Side Effects * None. * * Caveats * None. */ GCCError CConf:: GenerateEntityIDForAPEList ( CAppSap *pAppSap, PGCCSessionKey session_key, GCCEntityID *pEid ) { GCCError rc = GCC_ALLOCATION_FAILURE; CSessKeyContainer *pSessKey = NULL; // a must EntityID eidOriginal; ENROLLED_APE_INFO *enrolled_ape_info = NULL; // a must /* ** First find an entity id that has not been used. If all of the IDs ** are in use we return an allocation failure. */ *pEid = GCC_INVALID_EID; eidOriginal = m_nAPEEntityID; while (TRUE) { if (++m_nAPEEntityID != GCC_INVALID_EID) { if (NULL == m_EnrolledApeEidList2.Find(m_nAPEEntityID)) { // the new entity ID does not exist. job is done. *pEid = m_nAPEEntityID; break; } if (m_nAPEEntityID == eidOriginal) { ERROR_OUT(("CConf::GenerateEntityIDForAPEList: use up all entity IDs")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } } } ASSERT(GCC_INVALID_EID != *pEid); /* ** Now if no errors occured we will create the enrolled APE info structure ** that will be stored in the enrolled APE list. */ // // LONCHANC: We should avoid this memory allocation. ENROLLED_APE_INFO has only 2 dwords! // DBG_SAVE_FILE_LINE enrolled_ape_info = new ENROLLED_APE_INFO; if (NULL == enrolled_ape_info) { ERROR_OUT(("CConf::GenerateEntityIDForAPEList: can't create ENROLLED_APE_INFO")); ASSERT(GCC_ALLOCATION_FAILURE == rc); goto MyExit; } enrolled_ape_info->pAppSap = pAppSap; DBG_SAVE_FILE_LINE pSessKey = new CSessKeyContainer(session_key, &rc); if (pSessKey != NULL && rc == GCC_NO_ERROR) { enrolled_ape_info->session_key = pSessKey; m_EnrolledApeEidList2.Append(*pEid, enrolled_ape_info); } MyExit: if (GCC_NO_ERROR != rc) { delete enrolled_ape_info; if (NULL != pSessKey) { pSessKey->Release(); } } return rc; } /* * CConf::RemoveSAPFromAPEList () * * Private Function Description * This routine takes care of removing all the references to a single * sap from the m_EnrolledApeEidList2. It is also responsible for unenrolling * all of these APEs from the appropriate Application SAPs. * * Formal Parameters: * hSap - (i) SAP handle to remove and unenroll. * * Return Value * None. * * Side Effects * None. * * Caveats * None. */ void CConf:: RemoveSAPFromAPEList ( CAppSap *pAppSap ) { GCCEntityID eid; ENROLLED_APE_INFO *lpEnrAPEInfo; /* ** We make a temporary copy of the list here so that we can remove ** members from it while we are iterating on it. */ while (NULL != (lpEnrAPEInfo = GetEnrolledAPEbySap(pAppSap, &eid))) { CAppRosterMgr *lpAppRosterMgr; /* ** Here we remove the entities associated with this application ** from any application rosters it is enrolled in. */ m_AppRosterMgrList.Reset(); while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate())) { TRACE_OUT(("CConf::RemoveSAPFromAPEList: remove entity = %d", (int) eid)); lpAppRosterMgr->RemoveEntityReference(eid); } /* ** We must remove any references to this SAP from the ** registry so that any outstanding request by this SAP ** will not be processed. */ m_pAppRegistry->UnEnrollAPE(eid); /* ** Since this APE is no longer enrolled remove it from ** the list of APEs. */ DeleteEnrolledAPE(eid); } } /* * CConf::DoesSAPHaveEnrolledAPE () * * Private Function Description * This routine is responsible for determining if there is a single * (or multiple) APEs enrolled through the specified SAP handle. * * Formal Parameters: * sap_handle - (i) SAP handle of SAP being checked. * * Return Value * TRUE - If SAP does have an enrolled APE. * FALSE - If SAP does not have an enrolled APE. * * Side Effects * None. * * Caveats * None. */ ENROLLED_APE_INFO *CConf:: GetEnrolledAPEbySap ( CAppSap *pAppSap, GCCEntityID *pEid ) { ENROLLED_APE_INFO *pEnrAPEInfo; GCCEntityID eid; m_EnrolledApeEidList2.Reset(); while (NULL != (pEnrAPEInfo = m_EnrolledApeEidList2.Iterate(&eid))) { if (pAppSap == pEnrAPEInfo->pAppSap) { if (NULL != pEid) { *pEid = eid; } return pEnrAPEInfo; } } return NULL; } void CConf:: DeleteEnrolledAPE ( EntityID nEntityID ) { ENROLLED_APE_INFO *lpEnrAPEInfo; if (NULL != (lpEnrAPEInfo = m_EnrolledApeEidList2.Remove(nEntityID))) { if (NULL != lpEnrAPEInfo->session_key) { lpEnrAPEInfo->session_key->Release(); } delete lpEnrAPEInfo; } } /* * GCCError FlushRosterData() * * Private Function Description * This routine flushes all the application roster managers of any PDU data * that might be queued up. This also gives the roster managers a chance * to deliver roster update if necessary. Note that we build and deliver * the high level portion of the PDU here. Since only application roster * stuff will be sent in this pdu we must set the pointer to the conference * information to NULL so that the encoder wont try to encode it. */ GCCError CConf:: AsynchFlushRosterData ( void ) { if (NULL != g_pControlSap) { AddRef(); ::PostMessage(g_pControlSap->GetHwnd(), CONF_FLUSH_ROSTER_DATA, 0, (LPARAM) this); } return GCC_NO_ERROR; } void CConf:: WndMsgHandler ( UINT uMsg ) { if (CONF_FLUSH_ROSTER_DATA == uMsg) { FlushRosterData(); // // We AddRef while posting the message. // Release(); } else { ERROR_OUT(("WndMsgHandler: invalid msg=%u", uMsg)); } } GCCError CConf:: FlushRosterData ( void ) { GCCError rc = GCC_NO_ERROR; DebugEntry(CConf::FlushRosterData); if (m_fConfIsEstablished) { GCCPDU gcc_pdu; CAppRosterMgrList RosterMgrDeleteList; CAppRosterMgr *lpAppRosterMgr; gcc_pdu.choice = INDICATION_CHOSEN; gcc_pdu.u.indication.choice = ROSTER_UPDATE_INDICATION_CHOSEN; gcc_pdu.u.indication.u.roster_update_indication.refresh_is_full = FALSE; gcc_pdu.u.indication.u.roster_update_indication.application_information= NULL; // First get any CConf Roster PDU data that exists. rc = m_pConfRosterMgr->FlushRosterUpdateIndication( &gcc_pdu.u.indication.u.roster_update_indication.node_information); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::FlushRosterData: can't flush conf roster update, rc=%d", rc)); goto MyExit; } if (IsReadyToSendAppRosterUpdate()) { PSetOfApplicationInformation *ppSetOfAppInfo; PSetOfApplicationInformation pNextSetOfInfo; // Set up the pointer to the first application information. ppSetOfAppInfo = &gcc_pdu.u.indication.u.roster_update_indication.application_information; /* ** Here we iterate through all the application roster managers ** giving each a chance to append their roster updates to the ** roster update PDU and to deliver any necessary roster update ** messages. */ m_AppRosterMgrList.Reset(); while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate())) { pNextSetOfInfo = lpAppRosterMgr->FlushRosterUpdateIndication(ppSetOfAppInfo, &rc); if (GCC_NO_ERROR != rc) { ERROR_OUT(("CConf::FlushRosterData: can't flush app roster update, rc=%d", rc)); goto MyExit; } if (NULL != pNextSetOfInfo) { ppSetOfAppInfo = &pNextSetOfInfo->next; } /* ** Here we add the application roster manager to the list of ** managers to delete. They we be deleted and removed from the ** roster manager list below after the PDU is delivered. */ if (lpAppRosterMgr->IsEmpty()) { m_AppRosterMgrList.Remove(lpAppRosterMgr); RosterMgrDeleteList.Append(lpAppRosterMgr); } } } // if ready-to-send-app-roster-update else { TRACE_OUT(("CConf::FlushRosterData: not ready to send app roster update")); TRACE_OUT(("cApps=%u, m_cEnrollRequests=%u", m_RegisteredAppSapList.GetCount(), m_cEnrollRequests)); } /* ** Here, if there are no errors and there is actual application ** information, we go ahead and send out the roster update. */ if (NULL != gcc_pdu.u.indication.u.roster_update_indication. application_information || NODE_NO_CHANGE_CHOSEN != gcc_pdu.u.indication.u.roster_update_indication. node_information.node_record_list.choice) { TRACE_OUT(("CConf::FlushRosterData: sending roster update indication to mcs")); m_pMcsUserObject->RosterUpdateIndication( &gcc_pdu, IsConfTopProvider() ? FALSE : TRUE); } /* ** Here we cleanup any empty roster managers. Note that we must do this ** after delivering the PDU to avoid deleting a roster manager before ** using data associated with it (data obtained in the flush). */ RosterMgrDeleteList.DeleteList(); } // if m_fConfIsEstablished ASSERT(GCC_NO_ERROR == rc); MyExit: DebugExitINT(CConf::FlushRosterData, rc); return rc; } #define MIN_REGISTERED_APPS 2 BOOL CConf:: IsReadyToSendAppRosterUpdate ( void ) { if (m_fFirstAppRosterSent) { TRACE_OUT_EX(ZONE_T120_APP_ROSTER, ("CConf::IsReadyToSendAppRosterUpdate: " "YES \r\n")); return TRUE; } BOOL fRet = TRUE; if (NULL != m_pConfStartupAlarm && m_pConfStartupAlarm->IsExpired()) { TRACE_OUT_EX(ZONE_T120_APP_ROSTER, ("CConf::IsReadyToSendAppRosterUpdate: " "YES \r\n")); // fRet = TRUE; } else { UINT cApps = m_RegisteredAppSapList.GetCount(); if (cApps >= MIN_REGISTERED_APPS && (int) cApps <= m_cEnrollRequests) { TRACE_OUT_EX(ZONE_T120_APP_ROSTER, ("CConf::IsReadyToSendAppRosterUpdate: " "YES \r\n", cApps, m_cEnrollRequests)); // fRet = TRUE; } else { TRACE_OUT_EX(ZONE_T120_APP_ROSTER, ("CConf::IsReadyToSendAppRosterUpdate: " "NO \r\n", cApps, m_cEnrollRequests)); fRet = FALSE; } } if (fRet) { m_fFirstAppRosterSent = TRUE; delete m_pConfStartupAlarm; m_pConfStartupAlarm = NULL; } return fRet; } // look for this node ID in the roster's record set. BOOL CConf:: IsThisNodeParticipant ( GCCNodeID nid ) { return ((NULL != m_pConfRosterMgr) ? m_pConfRosterMgr->IsThisNodeParticipant(nid) : FALSE); } void CConfList:: DeleteList ( void ) { CConf *pConf; while (NULL != (pConf = Get())) { pConf->Release(); } } void CConfList2:: DeleteList ( void ) { CConf *pConf; while (NULL != (pConf = Get())) { pConf->Release(); } }