Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5948 lines
150 KiB

#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_GCCNC);
/*
* conf2.cpp
*
* Copyright (c) 1995 by DataBeam Corporation, Lexington, KY
*
* Abstract:
* This is the second part of 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.
*
* This second part of the implementation file deals mainly with the
* command target calls and any callbacks received by the Owner Callback
* function. It also contains many of the utility functions used by the
* conference object.
*
* FOR A MORE DETAILED EXPLANATION OF THIS CLASS SEE THE INTERFACE FILE.
*
*
* Private Instance Variables
*
* ALL PRIVATE INSTANCE VARIABLES ARE DEFINED IN CONF.CPP
*
* Portable:
* Yes
*
* Caveats:
* None.
*
* Author:
* blp
*/
#include "conf.h"
#include "gcontrol.h"
#include "translat.h"
#include "ogcccode.h"
#include "string.h"
#include <iappldr.h>
#define FT_VERSION_STR "MS FT Version"
#define WB_VERSION_STR "MS WB Version"
#define CHAT_VERSION_STR "MS CHAT Version"
OSTR FT_VERSION_ID = {sizeof(FT_VERSION_STR), (unsigned char*)FT_VERSION_STR};
OSTR WB_VERSION_ID = {sizeof(WB_VERSION_STR), (unsigned char*)WB_VERSION_STR};
OSTR CHAT_VERSION_ID = {sizeof(CHAT_VERSION_STR), (unsigned char*)CHAT_VERSION_STR};
#define TERMINATE_TIMER_DURATION 10000 // Duration in milliseconds
static const struct ASN1objectidentifier_s WB_ASN1_OBJ_IDEN[6] = {
{ (ASN1objectidentifier_t) &(WB_ASN1_OBJ_IDEN[1]), 0 },
{ (ASN1objectidentifier_t) &(WB_ASN1_OBJ_IDEN[2]), 0 },
{ (ASN1objectidentifier_t) &(WB_ASN1_OBJ_IDEN[3]), 20 },
{ (ASN1objectidentifier_t) &(WB_ASN1_OBJ_IDEN[4]), 126 },
{ (ASN1objectidentifier_t) &(WB_ASN1_OBJ_IDEN[5]), 0 },
{ NULL, 1 }
};
static const struct Key WB_APP_PROTO_KEY = {
1, (ASN1objectidentifier_t)&WB_ASN1_OBJ_IDEN};
static const struct ASN1objectidentifier_s FT_ASN1_OBJ_IDEN[6] = {
{ (ASN1objectidentifier_t) &(FT_ASN1_OBJ_IDEN[1]), 0 },
{ (ASN1objectidentifier_t) &(FT_ASN1_OBJ_IDEN[2]), 0 },
{ (ASN1objectidentifier_t) &(FT_ASN1_OBJ_IDEN[3]), 20 },
{ (ASN1objectidentifier_t) &(FT_ASN1_OBJ_IDEN[4]), 127 },
{ (ASN1objectidentifier_t) &(FT_ASN1_OBJ_IDEN[5]), 0 },
{ NULL, 1 }
};
static const struct Key FT_APP_PROTO_KEY = {
1, (ASN1objectidentifier_t)&FT_ASN1_OBJ_IDEN};
struct Key CHAT_APP_PROTO_KEY;
/*
* 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;
extern MCSDLLInterface *g_pMCSIntf;
/*
** These are GCCCommandTarget Calls. The only command targets that
** conference is connected to are Application SAPs and the Control SAP, so
** these Public member functions are only called from above.
*/
/*
* CConf::ConfJoinReqResponse()
*
* Public Function Description
* This routine is called when a node controller responds to a join
* request that was issued by a join from a node connected to a subnode.
*/
GCCError CConf::
ConfJoinReqResponse
(
UserID receiver_id,
CPassword *password_challenge,
CUserDataListContainer *user_data_list,
GCCResult result
)
{
DebugEntry(CConf::ConfJoinReqResponse);
/*
** Since the joining node is not directly connected to this
** node we send the response back through the user channel.
** It is the user attachment objects responsibility to
** encode this PDU.
*/
if (m_pMcsUserObject != NULL)
{
m_pMcsUserObject->ConferenceJoinResponse(
receiver_id,
m_fClearPassword,
m_fConfLocked,
m_fConfListed,
m_eTerminationMethod,
password_challenge,
user_data_list,
result);
}
DebugExitINT(CConf::ConfJoinReqResponse, GCC_NO_ERROR);
return (GCC_NO_ERROR);
}
/*
* CConf::ConfInviteRequest()
*
* Public Function Description
* This routine is called from the owner object when a
* ConfInviteRequest primitive needs to be processed.
*/
GCCError CConf::
ConfInviteRequest
(
LPWSTR pwszCallerID,
TransportAddress calling_address,
TransportAddress called_address,
BOOL fSecure,
CUserDataListContainer *user_data_list,
PConnectionHandle connection_handle
)
{
GCCError rc = GCC_NO_ERROR;
PUChar encoded_pdu;
UINT encoded_pdu_length;
MCSError mcs_error;
ConnectGCCPDU connect_pdu;
INVITE_REQ_INFO *invite_request_info;
DebugEntry(CConf::ConfInviteRequest);
if (! m_fConfIsEstablished)
{
ERROR_OUT(("CConf::ConfInviteRequest: Conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
goto MyExit;
}
/*
** If the conference is locked, we only allow invite request if there
** are outstanding adds. T.124 states that when a conference is
** locked you can only use ADD to bring in new nodes to the conference.
*/
if (m_fConfLocked && m_AddResponseList.IsEmpty())
{
WARNING_OUT(("CConf::ConfInviteRequest: Conference is locked"));
rc = GCC_INVALID_CONFERENCE;
goto MyExit;
}
// Create the ConfInviteRequest PDU here.
connect_pdu.choice = CONFERENCE_INVITE_REQUEST_CHOSEN;
connect_pdu.u.conference_invite_request.bit_mask = 0;
/*
** First get the numeric and text (if it exists) portion of the
** conference name.
*/
connect_pdu.u.conference_invite_request.conference_name.bit_mask =0;
::lstrcpyA(connect_pdu.u.conference_invite_request.conference_name.numeric,
m_pszConfNumericName);
if (m_pwszConfTextName != NULL)
{
connect_pdu.u.conference_invite_request.conference_name.bit_mask |=
CONFERENCE_NAME_TEXT_PRESENT;
connect_pdu.u.conference_invite_request.conference_name.conference_name_text.value =
m_pwszConfTextName;
connect_pdu.u.conference_invite_request.conference_name.conference_name_text.length =
::lstrlenW(m_pwszConfTextName);
}
// Now set up the privilege list PDU data
if (m_pConductorPrivilegeList != NULL)
{
rc = m_pConductorPrivilegeList->GetPrivilegeListPDU(
&connect_pdu.u.conference_invite_request.cirq_conductor_privs);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfInviteRequest: can't get conductor privilege list, rc=%d", rc));
goto MyExit;
}
connect_pdu.u.conference_invite_request.bit_mask |= CIRQ_CONDUCTOR_PRIVS_PRESENT;
}
if (m_pConductModePrivilegeList != NULL)
{
rc = m_pConductModePrivilegeList->GetPrivilegeListPDU(
&connect_pdu.u.conference_invite_request.cirq_conducted_privs);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfInviteRequest: can't get conduct mode privilege list, rc=%d", rc));
goto MyExit;
}
connect_pdu.u.conference_invite_request.bit_mask |= CIRQ_CONDUCTED_PRIVS_PRESENT;
}
if (m_pNonConductModePrivilegeList != NULL)
{
rc = m_pNonConductModePrivilegeList->GetPrivilegeListPDU(
&connect_pdu.u.conference_invite_request.cirq_non_conducted_privs);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfInviteRequest: can't get non-conduct mode privilege list, rc=%d", rc));
goto MyExit;
}
connect_pdu.u.conference_invite_request.bit_mask |= CIRQ_NON_CONDUCTED_PRIVS_PRESENT;
}
if (m_pwszConfDescription != NULL)
{
connect_pdu.u.conference_invite_request.cirq_description.value =
m_pwszConfDescription;
connect_pdu.u.conference_invite_request.cirq_description.length =
::lstrlenW(m_pwszConfDescription);
connect_pdu.u.conference_invite_request.bit_mask |= CIRQ_DESCRIPTION_PRESENT;
}
if (pwszCallerID != NULL)
{
connect_pdu.u.conference_invite_request.cirq_caller_id.value = pwszCallerID;
connect_pdu.u.conference_invite_request.cirq_caller_id.length = ::lstrlenW(pwszCallerID);
connect_pdu.u.conference_invite_request.bit_mask |= CIRQ_CALLER_ID_PRESENT;
}
if (user_data_list != NULL)
{
rc = user_data_list->GetUserDataPDU(
&connect_pdu.u.conference_invite_request.cirq_user_data);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfInviteRequest: can't get user data, rc=%d", rc));
goto MyExit;
}
connect_pdu.u.conference_invite_request.bit_mask |= CIRQ_USER_DATA_PRESENT;
}
connect_pdu.u.conference_invite_request.node_id = m_pMcsUserObject->GetMyNodeID();
connect_pdu.u.conference_invite_request.top_node_id = m_pMcsUserObject->GetTopNodeID();
connect_pdu.u.conference_invite_request.tag = GetNewUserIDTag();
connect_pdu.u.conference_invite_request.clear_password_required = (ASN1bool_t)m_fClearPassword;
connect_pdu.u.conference_invite_request.conference_is_locked = (ASN1bool_t)m_fConfLocked;
connect_pdu.u.conference_invite_request.conference_is_conductible = (ASN1bool_t)m_fConfConductible;
connect_pdu.u.conference_invite_request.conference_is_listed = (ASN1bool_t)m_fConfListed;
connect_pdu.u.conference_invite_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::ConfInviteRequest: 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,
FALSE, // Downward connection
encoded_pdu,
encoded_pdu_length,
connection_handle,
m_pDomainParameters,
this);
g_GCCCoder->FreeEncoded(encoded_pdu);
if (MCS_NO_ERROR != mcs_error)
{
ERROR_OUT(("CConf::ConfInviteRequest: ConnectProviderRequest 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;
}
/*
** Add the user's tag number to the list of
** outstanding user ids along with its associated
** connection.
*/
m_ConnHdlTagNumberList2.Append(connect_pdu.u.conference_invite_request.tag, *connection_handle);
// Add connection handle to the list of connections
ASSERT(0 != *connection_handle);
m_ConnHandleList.Append(*connection_handle);
/*
** Add the connection handle and the Node Id tag to
** the list of outstanding invite request.
*/
DBG_SAVE_FILE_LINE
invite_request_info = new INVITE_REQ_INFO;
if (NULL == invite_request_info)
{
ERROR_OUT(("CConf::ConfInviteRequest: can't create invite request info"));
rc = GCC_ALLOCATION_FAILURE;
goto MyExit;
}
invite_request_info->connection_handle = *connection_handle;
invite_request_info->invite_tag = m_nUserIDTagNumber;
invite_request_info->user_data_list = NULL;
m_InviteRequestList.Append(invite_request_info);
// Free the privilege list packed into structures for encoding
if (connect_pdu.u.conference_invite_request.bit_mask & CIRQ_CONDUCTOR_PRIVS_PRESENT)
{
m_pConductorPrivilegeList->FreePrivilegeListPDU(
connect_pdu.u.conference_invite_request.cirq_conductor_privs);
}
if (connect_pdu.u.conference_invite_request.bit_mask & CIRQ_CONDUCTED_PRIVS_PRESENT)
{
m_pConductModePrivilegeList->FreePrivilegeListPDU(
connect_pdu.u.conference_invite_request.cirq_conducted_privs);
}
if (connect_pdu.u.conference_invite_request.bit_mask & CIRQ_NON_CONDUCTED_PRIVS_PRESENT)
{
m_pNonConductModePrivilegeList->FreePrivilegeListPDU(
connect_pdu.u.conference_invite_request.cirq_non_conducted_privs);
}
ASSERT(GCC_NO_ERROR == rc);
MyExit:
DebugExitINT(CConf::ConfInviteRequest, rc);
return rc;
}
/*
* CConf::ConfLockRequest()
*
* Public Function Description:
* This routine is called from Control Sap when a
* ConfLockRequest primitive needs to be processed.
*/
#ifdef JASPER
GCCError CConf::
ConfLockRequest ( void )
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfLockRequest);
if (m_fConfIsEstablished)
{
if (m_fConfLocked == CONFERENCE_IS_NOT_LOCKED)
{
if (IsConfTopProvider())
{
ProcessConferenceLockRequest((UserID)m_pMcsUserObject->GetMyNodeID());
}
else
{
rc = m_pMcsUserObject->SendConferenceLockRequest();
}
}
else // the conference is already locked
{
#ifdef JASPER
g_pControlSap->ConfLockConfirm(GCC_RESULT_CONFERENCE_ALREADY_LOCKED, m_nConfID);
#endif // JASPER
}
}
else
{
ERROR_OUT(("CConf::ConfLockRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfLockRequest, rc);
return rc;
}
#endif // JASPER
/*
* CConf::ConfLockResponse()
*
* Public Function Description:
* This routine is called from Control Sap when a
* ConfLockResponse primitive needs to be processed.
*/
GCCError CConf::
ConfLockResponse
(
UserID requesting_node,
GCCResult result
)
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfLockResponse);
if (m_fConfIsEstablished)
{
if (requesting_node == m_pMcsUserObject->GetTopNodeID())
{
#ifdef JASPER
g_pControlSap->ConfLockConfirm(result, m_nConfID);
#endif // JASPER
}
else
{
rc = m_pMcsUserObject->SendConferenceLockResponse(requesting_node, result);
}
if (rc == GCC_NO_ERROR && result == GCC_RESULT_SUCCESSFUL)
{
m_fConfLocked = CONFERENCE_IS_LOCKED;
rc = m_pMcsUserObject->SendConferenceLockIndication(
TRUE, //indicates uniform send
0);
}
}
else
{
ERROR_OUT(("CConf::ConfLockResponse: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfLockResponse, rc);
return rc;
}
/*
* CConf::ConfUnlockRequest()
*
* Public Function Description:
* This routine is called from Control Sap when a
* ConferenceUnlockRequest primitive needs to be processed.
*/
#ifdef JASPER
GCCError CConf::
ConfUnlockRequest ( void )
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfUnlockRequest);
if (m_fConfIsEstablished)
{
if (m_fConfLocked == CONFERENCE_IS_LOCKED)
{
if (IsConfTopProvider())
{
ProcessConferenceUnlockRequest((UserID)m_pMcsUserObject->GetMyNodeID());
}
else
{
rc = m_pMcsUserObject->SendConferenceUnlockRequest();
}
}
else // the conference is already unlocked
{
#ifdef JASPER
g_pControlSap->ConfUnlockConfirm(GCC_RESULT_CONFERENCE_ALREADY_UNLOCKED, m_nConfID);
#endif // JASPER
}
}
else
{
ERROR_OUT(("CConf::ConfUnlockRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfUnlockRequest, rc);
return rc;
}
#endif // JASPER
/*
* CConf::ConfUnlockResponse()
*
* Public Function Description:
* This routine is called from Control Sap when a
* ConfUnlockResponse primitive needs to be processed.
*/
#ifdef JASPER
GCCError CConf::
ConfUnlockResponse
(
UserID requesting_node,
GCCResult result
)
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfUnlockResponse);
if (m_fConfIsEstablished)
{
if (requesting_node == m_pMcsUserObject->GetTopNodeID())
{
#ifdef JASPER
g_pControlSap->ConfUnlockConfirm(result, m_nConfID);
#endif // JASPER
}
else
{
rc = m_pMcsUserObject->SendConferenceUnlockResponse(requesting_node, result);
}
if (rc == GCC_NO_ERROR && result == GCC_RESULT_SUCCESSFUL)
{
m_fConfLocked = CONFERENCE_IS_NOT_LOCKED;
rc = m_pMcsUserObject->SendConferenceUnlockIndication(
TRUE, //indicates uniform send
0);
}
}
else
{
ERROR_OUT(("CConf::ConfUnlockResponse: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfUnlockResponse, rc);
return rc;
}
#endif // JASPER
/*
* CConf::ConfEjectUserRequest ()
*
* Private Function Description
* This function initiates an eject user for the specified node id.
*/
GCCError CConf::
ConfEjectUserRequest
(
UserID ejected_node_id,
GCCReason reason
)
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfEjectUserRequest);
if (m_fConfIsEstablished)
{
if (IsConfTopProvider())
{
if (IsThisNodeParticipant(ejected_node_id))
{
ConnectionHandle nConnHdl;
BOOL fChildNode = FALSE;
// First check to see if it is a child node that is being ejected.
m_ConnHandleList.Reset();
while (0 != (nConnHdl = m_ConnHandleList.Iterate()))
{
if (m_pMcsUserObject->GetUserIDFromConnection(nConnHdl) == ejected_node_id)
{
fChildNode = TRUE;
break;
}
}
if (fChildNode ||
DoesRequesterHavePrivilege(m_pMcsUserObject->GetMyNodeID(), EJECT_USER_PRIVILEGE))
{
// Add this ejected node to the list of Ejected Nodes
m_EjectedNodeConfirmList.Append(ejected_node_id);
/*
** The user attachment object decides where the ejct should
** be sent (either to the Top Provider or conference wide as
** an indication.
*/
m_pMcsUserObject->EjectNodeFromConference(ejected_node_id, reason);
}
else
{
#ifdef JASPER
/*
** The top provider does not have the privilege to eject
** a node from the conference. Send the appropriate
** confirm.
*/
g_pControlSap->ConfEjectUserConfirm(
m_nConfID,
ejected_node_id,
GCC_RESULT_INVALID_REQUESTER);
#endif // JASPER
rc = fChildNode ? GCC_INSUFFICIENT_PRIVILEGE : GCC_INVALID_MCS_USER_ID;
WARNING_OUT(("CConf::ConfEjectUserRequest: failed, rc=%d", rc));
}
}
else
{
rc = GCC_INVALID_MCS_USER_ID;
WARNING_OUT(("CConf::ConfEjectUserRequest: failed, rc=%d", rc));
}
}
else
{
// Add this ejected node to the list of Ejected Nodes
m_EjectedNodeConfirmList.Append(ejected_node_id);
/*
** The user attachment object decides where the ejct should
** be sent (either to the Top Provider or conference wide as
** an indication.
*/
m_pMcsUserObject->EjectNodeFromConference(ejected_node_id, reason);
}
}
else
{
ERROR_OUT(("CConf::ConfEjectUserRequest: conf not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfEjectUserRequest, rc);
return rc;
}
/*
* CConf::ConfAnnouncePresenceRequest ()
*
* Private Function Description
* This function forces a roster update indication and a confirm to be
* sent.
*/
GCCError CConf::
ConfAnnouncePresenceRequest ( PGCCNodeRecord node_record )
{
GCCError rc;
DebugEntry(CConf::ConfAnnouncePresenceRequest);
// If the conference is not established send back a negative confirm
if (! m_fConfIsEstablished)
{
WARNING_OUT(("CConf::ConfAnnouncePresenceRequest: conf not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
goto MyExit;
}
/*
** This takes care of setting up the nodes record in the
** appropriate conference roster.
*/
rc = m_pConfRosterMgr->AddNodeRecord(node_record);
if (GCC_NO_ERROR != rc)
{
TRACE_OUT(("CConf::ConfAnnouncePresenceRequest: updating previous record"));
rc = m_pConfRosterMgr->UpdateNodeRecord(node_record);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfAnnouncePresenceRequest: can't update node record, rc=%d", rc));
goto MyExit;
}
}
// Only flush the roster data here if there is no startup alarm.
rc = AsynchFlushRosterData();
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfAnnouncePresenceRequest: can't flush roster data, rc=%d", rc));
goto MyExit;
}
g_pControlSap->ConfAnnouncePresenceConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
ASSERT(GCC_NO_ERROR == rc);
MyExit:
DebugExitINT(CConf::ConfAnnouncePresenceRequest, rc);
return rc;
}
/*
* GCCError ConfDisconnectRequest ()
*
* Public Function Description
* This function initiates a disconnect of this node from the conference.
* This involves ejecting all subordinate nodes before actually
* disconnecting the parent connection.
*/
GCCError CConf::
ConfDisconnectRequest ( void )
{
GCCError rc = GCC_NO_ERROR;
UserID child_node_id;
ConnectionHandle nConnHdl;
DebugEntry(CConf::ConfDisconnectRequest);
/*
** Before we start the disconnect process we must remove all the
** outstanding invite request from our list and send back associated
** confirms. Here we go ahead disconnect all connection associated with
** the invites.
*/
DeleteOutstandingInviteRequests();
/*
** We set conference established to FALSE since the conference is
** no longer established (this also prevents a terminate indication
** from being sent).
*/
m_fConfIsEstablished = FALSE;
/*
** Iterate through the list of connection handles and eject each
** of the child nodes that is associated with it.
*/
m_ConnHandleList.Reset();
while (0 != (nConnHdl = m_ConnHandleList.Iterate()))
{
child_node_id = m_pMcsUserObject->GetUserIDFromConnection(nConnHdl);
rc = m_pMcsUserObject->EjectNodeFromConference (child_node_id,
GCC_REASON_HIGHER_NODE_DISCONNECTED);
if (rc != GCC_NO_ERROR)
{
ERROR_OUT(("CConf::ConfDisconnectRequest: can't eject node from conference"));
break;
}
}
// If there is an error we go ahead and do a hard disconnect
if (m_ConnHandleList.IsEmpty() || rc != GCC_NO_ERROR)
{
/*
** First inform the control SAP that this node has successfuly
** disconnected.
*/
rc = g_pControlSap->ConfDisconnectConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
// Tell the owner object to terminate this conference
InitiateTermination(GCC_REASON_NORMAL_TERMINATION, 0);
}
else
{
/*
** Wait for all the ejects to complete before the conference is
** terminated.
*/
m_fConfDisconnectPending = TRUE;
}
DebugExitINT(CConf::ConfDisconnectRequest, rc);
return rc;
}
/*
* GCCError ConfTerminateRequest ()
*
* Public Function Description
* This routine initiates a terminate sequence which starts with a request
* to the Top Provider if this node is not already the Top Provider.
*/
#ifdef JASPER
GCCError CConf::
ConfTerminateRequest ( GCCReason reason )
{
GCCError rc;
DebugEntry(CConf::ConfTerminateRequest);
if (m_fConfIsEstablished)
{
/*
** Before we start the termination process we must remove all the
** outstanding invite request from our list and send back associated
** confirms. Here we go ahead disconnect all connections associated
** with these invites.
*/
DeleteOutstandingInviteRequests();
if (IsConfTopProvider())
{
if (DoesRequesterHavePrivilege( m_pMcsUserObject->GetMyNodeID(),
TERMINATE_PRIVILEGE))
{
TRACE_OUT(("CConf::ConfTerminateRequest: Node has permission to terminate"));
/*
** Since the terminate was successful, we go ahead and
** set the m_fConfIsEstablished instance variable to
** FALSE. This prevents any other messages from flowing
** to the SAPs other than terminate messages.
*/
m_fConfIsEstablished = FALSE;
// Send the terminate confirm.
g_pControlSap->ConfTerminateConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
// This call takes care of both the local and remote terminate
m_pMcsUserObject->ConferenceTerminateIndication(reason);
}
else
{
WARNING_OUT(("CConf::ConfTerminateRequest: Node does NOT have permission to terminate"));
g_pControlSap->ConfTerminateConfirm(m_nConfID, GCC_RESULT_INVALID_REQUESTER);
}
}
else
{
m_pMcsUserObject->ConferenceTerminateRequest(reason);
}
rc = GCC_NO_ERROR;
}
else
{
ERROR_OUT(("CConf::ConfTerminateRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfTerminateRequest, rc);
return rc;
}
#endif // JASPER
/********************* Registry Calls ***********************************/
/*
* GCCError RegistryRegisterChannelRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class.
*/
GCCError CConf::
RegistryRegisterChannelRequest
(
PGCCRegistryKey registry_key,
ChannelID nChnlID,
CAppSap *pAppSap
)
{
GCCError rc;
EntityID eid;
DebugEntry(CConf::RegistryRegisterChannelRequest);
rc = GetEntityIDFromAPEList(pAppSap, &registry_key->session_key, &eid);
if (rc == GCC_NO_ERROR)
{
rc = m_pAppRegistry->RegisterChannel(registry_key, nChnlID, eid);
}
DebugExitINT(CConf::RegistryRegisterChannelRequest, rc);
return rc;
}
/*
* GCCError RegistryAssignTokenRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class.
*/
GCCError CConf::
RegistryAssignTokenRequest
(
PGCCRegistryKey registry_key,
CAppSap *pAppSap
)
{
GCCError rc;
GCCEntityID eid;
DebugEntry(CConf::RegistryAssignTokenRequest);
rc = GetEntityIDFromAPEList(pAppSap, &registry_key->session_key, &eid);
if (rc == GCC_NO_ERROR)
{
rc = m_pAppRegistry->AssignToken(registry_key, eid);
}
DebugExitINT(CConf::RegistryAssignTokenRequest, rc);
return rc;
}
/*
* GCCError RegistrySetParameterRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class.
*/
GCCError CConf::
RegistrySetParameterRequest
(
PGCCRegistryKey registry_key,
LPOSTR parameter_value,
GCCModificationRights modification_rights,
CAppSap *pAppSap
)
{
GCCError rc;
GCCEntityID eid;
DebugEntry(CConf::RegistrySetParameterRequest);
rc = GetEntityIDFromAPEList(pAppSap, &registry_key->session_key, &eid);
if (rc == GCC_NO_ERROR)
{
rc = m_pAppRegistry->SetParameter(registry_key,
parameter_value,
modification_rights,
eid);
}
DebugExitINT(CConf::RegistrySetParameterRequest, rc);
return rc;
}
/*
* GCCError RegistryRetrieveEntryRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class.
*/
GCCError CConf::
RegistryRetrieveEntryRequest
(
PGCCRegistryKey registry_key,
CAppSap *pAppSap
)
{
GCCError rc;
GCCEntityID eid;
DebugEntry(CConf::RegistryRetrieveEntryRequest);
rc = GetEntityIDFromAPEList(pAppSap, &registry_key->session_key, &eid);
if (rc == GCC_NO_ERROR)
{
rc = m_pAppRegistry->RetrieveEntry(registry_key, eid);
}
DebugExitINT(CConf::RegistryRetrieveEntryRequest, rc);
return rc;
}
/*
* GCCError RegistryDeleteEntryRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class.
*/
GCCError CConf::
RegistryDeleteEntryRequest
(
PGCCRegistryKey registry_key,
CAppSap *pAppSap
)
{
GCCError rc;
EntityID eid;
DebugEntry(CConf::RegistryDeleteEntryRequest);
rc = GetEntityIDFromAPEList(pAppSap, &registry_key->session_key, &eid);
if (rc == GCC_NO_ERROR)
{
rc = m_pAppRegistry->DeleteEntry(registry_key, eid);
}
DebugExitINT(CConf::RegistryDeleteEntryRequest, rc);
return rc;
}
/*
* GCCError RegistryMonitorRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class.
*/
GCCError CConf::
RegistryMonitorRequest
(
BOOL fEnableDelivery,
PGCCRegistryKey registry_key,
CAppSap *pAppSap)
{
GCCError rc;
GCCEntityID eid;
DebugEntry(CConf::RegistryMonitorRequest);
rc = GetEntityIDFromAPEList(pAppSap, &registry_key->session_key, &eid);
if (rc == GCC_NO_ERROR)
{
rc = m_pAppRegistry->MonitorRequest(registry_key, fEnableDelivery, eid);
}
DebugExitINT(CConf:RegistryMonitorRequest, rc);
return rc;
}
/*
* GCCError RegistryAllocateHandleRequest ()
*
* Public Function Description
* This initiates a registry request sequence. Note that the registry
* response is handled by the registry class. This registry call is
* a bit different from the other registry calls. Notice that there is
* no registry key associated with this call so there is no way to
* explicitly determine the entity ID. Luckily, the entity ID is not
* passed back in the allocate confirm so we just pick an entity id
* that is associated with this SAP. It makes no difference which one
* we pick because they all accomplish the same thing.
*/
GCCError CConf::
RegistryAllocateHandleRequest
(
UINT cHandles,
CAppSap *pAppSap
)
{
GCCError rc;
ENROLLED_APE_INFO *lpEnrAPEInfo;
GCCEntityID eid;
DebugEntry(CConf::RegistryAllocateHandleRequest);
// First we must find a single entity id that is associated with this SAP.
if (NULL != (lpEnrAPEInfo = GetEnrolledAPEbySap(pAppSap, &eid)))
{
ASSERT(GCC_INVALID_EID != eid);
rc = m_pAppRegistry->AllocateHandleRequest(cHandles, eid);
}
else
{
WARNING_OUT(("CConf::RegistryAllocateHandleRequest: Application not enrolled"));
rc = GCC_APP_NOT_ENROLLED;
}
DebugExitINT(CConf::RegistryAllocateHandleRequest, rc);
return rc;
}
/********************* Conductorship Calls ***********************************/
/*
* GCCError ConductorAssignRequest ()
*
* Public Function Description
* This initiates a Conductor assign request sequence. Here the node is
* requesting to become the conductor.
*/
#ifdef JASPER
GCCError CConf::
ConductorAssignRequest ( void )
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorAssignRequest);
// Return an error if the conference is not established.
if (m_fConfIsEstablished)
{
if (m_fConfConductible)
{
if (m_nConductorNodeID != m_pMcsUserObject->GetMyNodeID())
{
if ((m_nPendingConductorNodeID == 0) && ! m_fConductorGiveResponsePending)
{
m_fConductorAssignRequestPending = TRUE;
rc = m_pMcsUserObject->ConductorTokenGrab();
}
else
{
TRACE_OUT(("CConf::ConductorAssignConfirm:Give Pending"));
eResult = GCC_RESULT_CONDUCTOR_GIVE_IS_PENDING;
}
}
else
{
ERROR_OUT(("CConf::ConductorAssignRequest: Already Conductor"));
/*
** Since we are already the conductor send back a successful
** result
*/
//
// LONCHANC: Why not GCC_RESULT_ALREADY_CONDUCTOR?
//
eResult = GCC_RESULT_SUCCESSFUL;
}
}
else
{
ERROR_OUT(("CConf::ConductorAssignRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
}
else
{
ERROR_OUT(("CConf::ConductorAssignRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
#ifdef JASPER
if (INVALID_GCC_RESULT != eResult)
{
g_pControlSap->ConductorAssignConfirm(eResult, m_nConfID);
}
#endif // JASPER
DebugExitINT(CConf::ConductorAssignRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConductorReleaseRequest ()
*
* Public Function Description
* Here the node is attempting to give up conductorship.
*/
#ifdef JASPER
GCCError CConf::
ConductorReleaseRequest ( void )
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorReleaseRequest);
if (m_fConfConductible)
{
if (m_nConductorNodeID == m_pMcsUserObject->GetMyNodeID())
{
if (m_nPendingConductorNodeID == 0)
{
/*
** This does not seem right, but this is the way that T.124
** defines it should work.
*/
m_nConductorNodeID = 0; // Set back to non-conducted mode
m_fConductorGrantedPermission = FALSE;
rc = m_pMcsUserObject->SendConductorReleaseIndication();
if (rc == GCC_NO_ERROR)
{
rc = m_pMcsUserObject->ConductorTokenRelease();
/*
** Inform the control SAP and all the enrolled application
** SAPs that the conductor was released. We do this here
** because we will not process the release indication
** when it comes back in.
*/
if (rc == GCC_NO_ERROR)
{
g_pControlSap->ConductorReleaseIndication(m_nConfID);
/*
** We iterate on a temporary list to avoid any problems
** if the application sap leaves during the callback.
*/
CAppSap *pAppSap;
CAppSapList TempList(m_RegisteredAppSapList);
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
if (DoesSAPHaveEnrolledAPE(pAppSap))
{
pAppSap->ConductorReleaseIndication(m_nConfID);
}
}
}
}
}
else
{
TRACE_OUT(("CConf: ConductorReleaseRequest: Give Pending"));
eResult = GCC_RESULT_CONDUCTOR_GIVE_IS_PENDING;
}
}
else
{
ERROR_OUT(("CConf::ConductorReleaseRequest: Not the Conductor"));
eResult = GCC_RESULT_NOT_THE_CONDUCTOR;
}
}
else
{
ERROR_OUT(("CConf::ConductorReleaseRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
#ifdef JASPER
if (INVALID_GCC_RESULT != eResult)
{
g_pControlSap->ConductorReleaseConfirm(eResult, m_nConfID);
}
#endif // JASPER
DebugExitINT(CConf::ConductorReleaseRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConductorPleaseRequest ()
*
* Public Function Description
* Here the node is asking to be given conductorship.
*/
#ifdef JASPER
GCCError CConf::
ConductorPleaseRequest ( void )
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorPleaseRequest);
if (m_fConfConductible)
{
// Return an error if the conference is not established
if (m_nConductorNodeID != 0)
{
if (m_nConductorNodeID != m_pMcsUserObject->GetMyNodeID())
{
rc = m_pMcsUserObject->ConductorTokenPlease();
if (rc == GCC_NO_ERROR)
{
// Send back positive confirm if successful
eResult = GCC_RESULT_SUCCESSFUL;
}
}
else
{
WARNING_OUT(("CConf::ConductorPleaseRequest: already conductor"));
eResult = GCC_RESULT_ALREADY_CONDUCTOR;
}
}
else
{
ERROR_OUT(("CConf::ConductorPleaseRequest: not in conducted mode"));
eResult = GCC_RESULT_NOT_IN_CONDUCTED_MODE;
}
}
else
{
ERROR_OUT(("CConf::ConductorPleaseRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
#ifdef JASPER
if (INVALID_GCC_RESULT != eResult)
{
g_pControlSap->ConductorPleaseConfirm(eResult, m_nConfID);
}
#endif // JASPER
DebugExitINT(CConf::ConductorPleaseRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConductorGiveRequest ()
*
* Public Function Description
* The function is called when the conductor wants to pass off
* conductorship to a different node.
*/
#ifdef JASPER
GCCError CConf::
ConductorGiveRequest ( UserID recipient_node_id )
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorGiveRequest);
if (m_fConfConductible)
{
// Am I in conducted mode?
if (m_nConductorNodeID != 0)
{
// Am I the conductor?
if (m_nConductorNodeID == m_pMcsUserObject->GetMyNodeID())
{
if (recipient_node_id != m_pMcsUserObject->GetMyNodeID())
{
if (m_nPendingConductorNodeID == 0)
{
/*
** We don't assume that the recipient node is the new
** conductor until we get a confirm or an
** AssignIndication. The m_nPendingConductorNodeID is
** used to buffer the recipient until the give confirm
** is received.
*/
m_nPendingConductorNodeID = recipient_node_id;
rc = m_pMcsUserObject->ConductorTokenGive(recipient_node_id);
}
else
{
TRACE_OUT(("CConf::ConductorGiveRequest: conductor give is pending"));
eResult = GCC_RESULT_CONDUCTOR_GIVE_IS_PENDING;
}
}
else
{
WARNING_OUT(("CConf::ConductorGiveRequest: already conductor"));
eResult = GCC_RESULT_ALREADY_CONDUCTOR;
}
}
else
{
ERROR_OUT(("CConf::ConductorGiveRequest: not the conductor"));
eResult = GCC_RESULT_NOT_THE_CONDUCTOR;
}
}
else
{
ERROR_OUT(("CConf::ConductorGiveRequest: not in conduct mode"));
eResult = GCC_RESULT_NOT_IN_CONDUCTED_MODE;
}
}
else
{
ERROR_OUT(("CConf::ConductorGiveRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
#ifdef JASPER
if (INVALID_GCC_RESULT != eResult)
{
g_pControlSap->ConductorGiveConfirm(eResult, m_nConfID, recipient_node_id);
}
#endif // JASPER
DebugExitINT(CConf::ConductorGiveRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConductorGiveResponse ()
*
* Public Function Description
* This function gets called in response to a Conductor Give Indication.
* If result is success then this node is the new conductor.
*/
GCCError CConf::
ConductorGiveResponse ( GCCResult eResult )
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConductorGiveResponse);
if (! m_fConductorGiveResponsePending)
{
ERROR_OUT(("CConf::ConductorGiveResponse: no give response pending"));
rc = GCC_NO_GIVE_RESPONSE_PENDING;
goto MyExit;
}
m_fConductorGiveResponsePending = FALSE;
if (eResult == GCC_RESULT_SUCCESSFUL)
{
// Set the conductor id to my user id if the response is success.
m_nConductorNodeID = m_pMcsUserObject->GetMyNodeID();
// The new conductor always has permission.
m_fConductorGrantedPermission = TRUE;
/*
** We must perform the give response before sending the dummy to
** the top provider so that MCS knows that the conductor token
** belongs to this node.
*/
rc = m_pMcsUserObject->ConductorTokenGiveResponse(RESULT_SUCCESSFUL);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConductorGiveResponse: ConductorTokenGiveResponse failed, rc=%d", rc));
goto MyExit;
}
/*
** If this node is not the Top Provider, we must try to Give the
** Conductor token to the Top Provider. The Top Provider is used
** to issue the Assign Indication whenever the conductor changes
** hands.
*/
if (m_pMcsUserObject->GetMyNodeID() != m_pMcsUserObject->GetTopNodeID())
{
rc = m_pMcsUserObject->ConductorTokenGive(m_pMcsUserObject->GetTopNodeID());
}
else
{
/*
** Here we go ahead and send the assign indication because we
** are already at the Top Provider.
*/
rc = m_pMcsUserObject->SendConductorAssignIndication(m_nConductorNodeID);
}
}
else
{
// Inform that giver that we are not interested
rc = m_pMcsUserObject->ConductorTokenGiveResponse(RESULT_USER_REJECTED);
}
MyExit:
DebugExitINT(CConf::ConductorGiveResponse, rc);
return rc;
}
/*
* GCCError ConductorPermitAskRequest ()
*
* Public Function Description
* This call is made when a node wants to request permission from the
* conductor.
*/
#ifdef JASPER
GCCError CConf::
ConductorPermitAskRequest ( BOOL grant_permission )
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorPermitAskRequest);
if (m_fConfConductible)
{
// Am I in conducted mode?
if (m_nConductorNodeID != 0)
{
if (m_nConductorNodeID != m_pMcsUserObject->GetMyNodeID())
{
rc = m_pMcsUserObject->SendConductorPermitAsk(grant_permission);
if (rc == GCC_NO_ERROR)
{
eResult = GCC_RESULT_SUCCESSFUL;
}
}
else
{
WARNING_OUT(("CConf::ConductorPermitAskRequest: already conductor"));
eResult = GCC_RESULT_ALREADY_CONDUCTOR;
}
}
else
{
ERROR_OUT(("CConf::ConductorPermitAskRequest: not in conducted mode"));
eResult = GCC_RESULT_NOT_IN_CONDUCTED_MODE;
}
}
else
{
ERROR_OUT(("CConf::ConductorPermitAskRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
#ifdef JASPER
if (INVALID_GCC_RESULT != eResult)
{
g_pControlSap->ConductorPermitAskConfirm(eResult, grant_permission, m_nConfID);
}
#endif // JASPER
DebugExitINT(CConf::ConductorPermitAskRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConductorPermitGrantRequest ()
*
* Public Function Description
* This function is called when a conductor wishes to grant permission
* to a specific node or to a list of nodes.
*/
#ifdef JASPER
GCCError CConf::
ConductorPermitGrantRequest
(
UINT number_granted,
PUserID granted_node_list,
UINT number_waiting,
PUserID waiting_node_list
)
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorPermitGrantRequest);
if (m_fConfConductible)
{
// Am I in conducted mode?
if (m_nConductorNodeID != 0)
{
// Am I the conductor?
if (m_nConductorNodeID == m_pMcsUserObject->GetMyNodeID())
{
TRACE_OUT(("CConf: ConductorPermitGrantRequest: SEND: number_granted = %d", number_granted));
rc = m_pMcsUserObject->SendConductorPermitGrant(
number_granted,
granted_node_list,
number_waiting,
waiting_node_list);
if (rc == GCC_NO_ERROR)
{
eResult = GCC_RESULT_SUCCESSFUL;
}
}
else
{
ERROR_OUT(("CConf::ConductorPermitGrantRequest: not the conductor"));
eResult = GCC_RESULT_NOT_THE_CONDUCTOR;
}
}
else
{
ERROR_OUT(("CConf::ConductorPermitGrantRequest: not in conducted mode"));
eResult = GCC_RESULT_NOT_IN_CONDUCTED_MODE;
}
}
else
{
ERROR_OUT(("CConf::ConductorPermitGrantRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
#ifdef JASPER
if (INVALID_GCC_RESULT != eResult)
{
g_pControlSap->ConductorPermitGrantConfirm(eResult, m_nConfID);
}
#endif // JASPER
DebugExitINT(CConf::ConductorPermitGrantRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConductorInquireRequest ()
*
* Public Function Description
* This function is called when a node request conductorship information.
*/
GCCError CConf::
ConductorInquireRequest ( CBaseSap *pSap )
{
GCCError rc = GCC_NO_ERROR;
GCCResult eResult = INVALID_GCC_RESULT;
DebugEntry(CConf::ConductorInquireRequest);
if (m_fConfConductible)
{
if (m_nConductorNodeID != 0)
{
rc = m_pMcsUserObject->ConductorTokenTest();
/*
** We must "push" the command target to the to the list of
** outstanding conductor test request. When the test confirm
** comes back the command target will be "poped" of the list.
** Note that all test request must be processed in the order that
** they are requested.
*/
m_ConductorTestList.Append(pSap);
}
else
{
// If not in conducted mode send back NO conductor information
ERROR_OUT(("CConf::ConductorInquireRequest: not in conducted mode"));
eResult = GCC_RESULT_NOT_IN_CONDUCTED_MODE;
}
}
else
{
ERROR_OUT(("CConf::ConductorInquireRequest: not conductible"));
eResult = GCC_RESULT_NOT_CONDUCTIBLE;
}
if (INVALID_GCC_RESULT != eResult)
{
pSap->ConductorInquireConfirm(NULL,
eResult,
m_fConductorGrantedPermission,
FALSE,
m_nConfID);
}
DebugExitINT(CConf:ConductorInquireRequest, rc);
return rc;
}
/********************** Miscelaneous Finctions **********************/
/*
* GCCError ConferenceTimeRemainingRequest ()
*
* Public Function Description
* This function initiates a TimeRemainingRequest sequence.
*/
GCCError CConf::
ConferenceTimeRemainingRequest
(
UINT time_remaining,
UserID node_id
)
{
GCCError rc;
DebugEntry(CConf::ConferenceTimeRemainingRequest);
if (m_fConfIsEstablished)
{
rc = m_pMcsUserObject->TimeRemainingRequest(time_remaining, node_id);
#ifdef JASPER
if (rc == GCC_NO_ERROR)
{
g_pControlSap->ConfTimeRemainingConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
}
#endif // JASPER
}
else
{
ERROR_OUT(("CConf::ConferenceTimeRemainingRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConferenceTimeRemainingRequest, rc);
return rc;
}
/*
* GCCError ConfTimeInquireRequest ()
*
* Public Function Description
* This function initiates a ConfTimeInquireRequest sequence.
*/
#ifdef JASPER
GCCError CConf::
ConfTimeInquireRequest ( BOOL time_is_conference_wide )
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfTimeInquireRequest);
if (m_fConfIsEstablished)
{
if ((m_eNodeType == CONVENER_NODE) ||
(m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE)||
(m_eNodeType == JOINED_CONVENER_NODE))
{
g_pControlSap->ConfTimeInquireIndication(
m_nConfID,
time_is_conference_wide,
m_pMcsUserObject->GetMyNodeID());
}
else
{
rc = m_pMcsUserObject->TimeInquireRequest(time_is_conference_wide);
}
#ifdef JASPER
if (rc == GCC_NO_ERROR)
{
g_pControlSap->ConfTimeInquireConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
}
#endif // JASPER
}
else
{
ERROR_OUT(("CConf::ConfTimeInquireRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfTimeInquireRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConfExtendRequest ()
*
* Public Function Description
* This function initiates a ConfExtendRequest sequence.
*/
#ifdef JASPER
GCCError CConf::
ConfExtendRequest
(
UINT extension_time,
BOOL time_is_conference_wide
)
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfExtendRequest);
if (m_fConfIsEstablished)
{
if ((m_eNodeType == CONVENER_NODE) ||
(m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE)||
(m_eNodeType == JOINED_CONVENER_NODE))
{
#ifdef JASPER
g_pControlSap->ConfExtendIndication(
m_nConfID,
extension_time,
time_is_conference_wide,
m_pMcsUserObject->GetMyNodeID());
#endif // JASPER
}
else
{
rc = m_pMcsUserObject->ConferenceExtendIndication(
extension_time,
time_is_conference_wide);
}
#ifdef JASPER
if (rc == GCC_NO_ERROR)
{
g_pControlSap->ConfExtendConfirm(
m_nConfID,
extension_time,
GCC_RESULT_SUCCESSFUL);
}
#endif // JASPER
}
else
{
ERROR_OUT(("CConf::ConfExtendRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfExtendRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConfAssistanceRequest ()
*
* Public Function Description
* This function initiates a ConfAssistanceRequest sequence.
*/
#ifdef JASPER
GCCError CConf::
ConfAssistanceRequest
(
UINT number_of_user_data_members,
PGCCUserData *user_data_list
)
{
GCCError rc;
DebugEntry(CConf::ConfAssistanceRequest);
if (m_fConfIsEstablished)
{
rc = m_pMcsUserObject->ConferenceAssistanceIndication(
number_of_user_data_members,
user_data_list);
#ifdef JASPER
if (rc == GCC_NO_ERROR)
{
g_pControlSap->ConfAssistanceConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
}
#endif // JASPER
}
else
{
ERROR_OUT(("CConf::ConfAssistanceRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfAssistanceRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError AppInvokeRequest()
*
* Public Function Description
* This function initiates an ApplicationInvokeRequest sequence.
*/
GCCError CConf::
AppInvokeRequest
(
CInvokeSpecifierListContainer *invoke_list,
GCCSimpleNodeList *pNodeList,
CBaseSap *pSap,
GCCRequestTag nReqTag
)
{
GCCError rc;
DebugEntry(CConf::AppInvokeRequest);
if (m_fConfIsEstablished)
{
rc = m_pMcsUserObject->AppInvokeIndication(invoke_list, pNodeList);
if (rc == GCC_NO_ERROR)
{
pSap->AppInvokeConfirm(m_nConfID, invoke_list, GCC_RESULT_SUCCESSFUL, nReqTag);
}
}
else
{
ERROR_OUT(("CConf::AppInvokeRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::AppInvokeRequest, rc);
return rc;
}
/*
* GCCError TextMessageRequest ()
*
* Public Function Description
* This function initiates an TextMessageRequest sequence.
*/
#ifdef JASPER
GCCError CConf::
TextMessageRequest
(
LPWSTR pwszTextMsg,
UserID destination_node
)
{
GCCError rc;
DebugEntry(CConf::TextMessageRequest);
if (m_fConfIsEstablished)
{
if (destination_node != m_pMcsUserObject->GetMyNodeID())
{
rc = m_pMcsUserObject->TextMessageIndication(pwszTextMsg, destination_node);
#ifdef JASPER
if (rc == GCC_NO_ERROR)
{
g_pControlSap->TextMessageConfirm(m_nConfID, GCC_RESULT_SUCCESSFUL);
}
#endif // JASPER
}
else
{
WARNING_OUT(("CConf::TextMessageRequest: invalid user ID"));
rc = GCC_INVALID_MCS_USER_ID;
}
}
else
{
ERROR_OUT(("CConf::TextMessageRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::TextMessageRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConfTransferRequest ()
*
* Public Function Description
* This function initiates an ConfTransferRequest sequence.
*/
#ifdef JASPER
GCCError CConf::
ConfTransferRequest
(
PGCCConferenceName destination_conference_name,
GCCNumericString destination_conference_modifier,
CNetAddrListContainer *destination_address_list,
UINT number_of_destination_nodes,
PUserID destination_node_list,
CPassword *password
)
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::ConfTransferRequest);
if (m_fConfIsEstablished)
{
if (IsConfTopProvider())
{
if (DoesRequesterHavePrivilege( m_pMcsUserObject->GetMyNodeID(),
TRANSFER_PRIVILEGE))
{
rc = m_pMcsUserObject->ConferenceTransferIndication(
destination_conference_name,
destination_conference_modifier,
destination_address_list,
number_of_destination_nodes,
destination_node_list,
password);
#ifdef JASPER
if (rc == GCC_NO_ERROR)
{
g_pControlSap->ConfTransferConfirm(
m_nConfID,
destination_conference_name,
destination_conference_modifier,
number_of_destination_nodes,
destination_node_list,
GCC_RESULT_SUCCESSFUL);
}
#endif // JASPER
}
else
{
WARNING_OUT(("CConf::ConfTransferRequest: insufficient privilege to transfer conference"));
}
}
else
{
rc = m_pMcsUserObject->ConferenceTransferRequest(
destination_conference_name,
destination_conference_modifier,
destination_address_list,
number_of_destination_nodes,
destination_node_list,
password);
}
}
else
{
ERROR_OUT(("CConf::ConfTransferRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfTransferRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConfAddRequest ()
*
* Public Function Description
* This function initiates an ConfAddRequest sequence.
*/
#ifdef JASPER
GCCError CConf::
ConfAddRequest
(
CNetAddrListContainer *network_address_container,
UserID adding_node,
CUserDataListContainer *user_data_container
)
{
GCCError rc = GCC_NO_ERROR;
TagNumber conference_add_tag;
UserID target_node;
DebugEntry(CConf::ConfAddRequest);
if (! m_fConfIsEstablished)
{
ERROR_OUT(("CConf::ConfAddRequest: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
goto MyExit;
}
/*
** A node cannot tell itself to add because of the way the
** Add Response call works. Since an Add Response is sent non-
** uniformly directly to the node that made the request the response
** would never reach the requesting node. Therefore, this is flaged
** as an error condition here.
*/
if (adding_node == m_pMcsUserObject->GetMyNodeID())
{
ERROR_OUT(("CConf::ConfAddRequest: can't tell myself to add"));
rc = GCC_BAD_ADDING_NODE;
goto MyExit;
}
/*
** Note that the way the standard reads, it looks like you
** do not have to check the privileges for the top provider
** on an Add. We do though check to see if the Top Provider is
** making the request to a node other than the top provider. If
** not this is considered an error here.
*/
if (IsConfTopProvider())
{
/*
** If the adding node is zero at the top provider, this is
** the same as specifying ones self to be the adding node.
*/
if (adding_node == 0)
{
ERROR_OUT(("CConf::ConfAddRequest: can't tell myself to add"));
rc = GCC_BAD_ADDING_NODE;
goto MyExit;
}
else
{
target_node = adding_node;
}
}
else
{
target_node = m_pMcsUserObject->GetTopNodeID();
}
// First determine the conference add tag
while (1)
{
conference_add_tag = ++m_nConfAddRequestTagNumber;
if (NULL == m_AddRequestList.Find(conference_add_tag))
break;
}
// Send out the PDU
rc = m_pMcsUserObject->ConferenceAddRequest(
conference_add_tag,
m_pMcsUserObject->GetMyNodeID(),
adding_node,
target_node,
network_address_container,
user_data_container);
if (GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ConfAddRequest: ConferenceAddRequest failed, rc=%d", rc));
goto MyExit;
}
/*
** We must lock the network address to keep it from
** being deleted upon returning.
*/
if (network_address_container != NULL)
{
network_address_container->LockNetworkAddressList();
}
// Add this entry to the add request list.
m_AddRequestList.Append(conference_add_tag, network_address_container);
ASSERT(GCC_NO_ERROR == rc);
MyExit:
DebugExitINT(CConf::ConfAddRequest, rc);
return rc;
}
#endif // JASPER
/*
* GCCError ConfAddResponse ()
*
* Public Function Description
* This call is made in response to an Add indication. It is initiated
* by the Node Controller.
*/
GCCError CConf::
ConfAddResponse
(
GCCResponseTag add_response_tag,
UserID requesting_node,
CUserDataListContainer *user_data_container,
GCCResult result
)
{
GCCError rc;
TagNumber lTagNum;
DebugEntry(CConf::ConfAddResponse);
if (m_fConfIsEstablished)
{
if (0 != (lTagNum = m_AddResponseList.Find(add_response_tag)))
{
// Send out the response PDU
rc = m_pMcsUserObject->ConferenceAddResponse(lTagNum, requesting_node,
user_data_container, result);
if (rc == GCC_NO_ERROR)
{
m_AddResponseList.Remove(add_response_tag);
}
else
{
ERROR_OUT(("CConf::ConfAddResponse: ConferenceAddResponse failed, rc=%d", rc));
}
}
else
{
ERROR_OUT(("CConf::ConfAddResponse: invalid add response tag"));
rc = GCC_INVALID_ADD_RESPONSE_TAG;
}
}
else
{
ERROR_OUT(("CConf::ConfAddResponse: conference not established"));
rc = GCC_CONFERENCE_NOT_ESTABLISHED;
}
DebugExitINT(CConf::ConfAddResponse, rc);
return rc;
}
/*
** These calls are received from the User Attachment object via the
** Owner-Callback routine. Note that all calls received from the
** user attachment object are preceeded by the word Process.
*/
/*
* CConf::ProcessRosterUpdateIndication ()
*
* Private Function Description
* This routine is responsible for processing all the incomming roster
* update PDUs which are received from subordinate nodes. These
* roster updates typically only include additions, changes or deletions
* of a few records within each PDU.
*
* Formal Parameters:
* roster_update - This is the PDU structure that contains the data
* associated with the roster update.
* sender_id - User ID of node that sent the roster update.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessRosterUpdatePDU
(
PGCCPDU roster_update,
UserID sender_id
)
{
GCCError err = GCC_NO_ERROR;
DebugEntry(CConf::ProcessRosterUpdatePDU);
if (m_pConfRosterMgr != NULL)
{
err = m_pConfRosterMgr->RosterUpdateIndication(roster_update, sender_id);
if (err != GCC_NO_ERROR)
{
goto MyExit;
}
// Process the whole PDU before performing the flush.
err = ProcessAppRosterIndicationPDU(roster_update, sender_id);
if (err != GCC_NO_ERROR)
{
goto MyExit;
}
UpdateNodeVersionList(roster_update, sender_id);
if (HasNM2xNode())
{
T120_QueryApplet(APPLET_ID_CHAT, APPLET_QUERY_NM2xNODE);
}
#ifdef CHECK_VERSION
if (GetNodeVersion(sender_id) >= NM_T120_VERSION_3) // after NM 3.0
{
if (!m_fFTEnrolled)
{
m_fFTEnrolled = DoesRosterPDUContainApplet(roster_update,
&FT_APP_PROTO_KEY, FALSE);
if (m_fFTEnrolled)
{
::T120_LoadApplet(APPLET_ID_FT, FALSE, m_nConfID, FALSE, NULL);
}
}
}
#else
if (!m_fFTEnrolled)
{
m_fFTEnrolled = DoesRosterPDUContainApplet(roster_update,
&FT_APP_PROTO_KEY, FALSE);
if (m_fFTEnrolled)
{
::T120_LoadApplet(APPLET_ID_FT, FALSE, m_nConfID, FALSE, NULL);
}
}
#endif // CHECK_VERSION
if (!m_fWBEnrolled)
{
m_fWBEnrolled = DoesRosterPDUContainApplet(roster_update,
&WB_APP_PROTO_KEY, FALSE);
if (m_fWBEnrolled)
{
::T120_LoadApplet(APPLET_ID_WB, FALSE, m_nConfID, FALSE, NULL);
}
}
if (!m_fChatEnrolled)
{
m_fChatEnrolled = DoesRosterPDUContainApplet(roster_update,
&CHAT_APP_PROTO_KEY, FALSE);
if (m_fChatEnrolled)
{
::T120_LoadApplet(APPLET_ID_CHAT, FALSE, m_nConfID, FALSE, NULL);
}
}
/*
** If this is the top provider and we are adding new nodes
** then we must update the new node with various roster
** information. That is what is going on here. If no new
** nodes have been added we go ahead and perform the
** Flush here.
*/
if (IsConfTopProvider() &&
roster_update->u.indication.u.roster_update_indication.node_information.nodes_are_added)
{
err = UpdateNewConferenceNode ();
}
else
{
//
// We just got an roster update from the wire.
//
err = FlushRosterData();
}
}
MyExit:
if (err != GCC_NO_ERROR)
{
ERROR_OUT(("CConf::ProcessRosterUpdatePDU: error processing roster refresh indication"));
InitiateTermination(GCC_REASON_ERROR_TERMINATION, 0);
}
DebugExitVOID(CConf::ProcessRosterUpdatePDU);
}
/*
* GCCError ProcessAppRosterIndicationPDU ()
*
* Private Function Description
* This function operates specifically on the application roster
* portion of a roster PDU.
*
* Formal Parameters:
* roster_update - This is the PDU structure that contains the data
* associated with the roster update.
* sender_id - User ID of node that sent the roster update.
*
* Return Value
* GCC_NO_ERROR - No error.
* GCC_ALLOCATION_FAILURE - A resource error occured.
* GCC_BAD_SESSION_KEY - A bad session key exists in the update.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
GCCError CConf::
ProcessAppRosterIndicationPDU
(
PGCCPDU roster_update,
UserID sender_id
)
{
GCCError rc = GCC_NO_ERROR;
PSetOfApplicationInformation set_of_application_info;
CAppRosterMgr *app_roster_manager;
CAppRosterMgr *new_app_roster_manager;
PSessionKey session_key;
DebugEntry(CConf::ProcessAppRosterIndicationPDU);
set_of_application_info = roster_update->u.indication.u.
roster_update_indication.application_information;
/*
** First we iterate through the complete set of application information
** to determine if there is information here for an application roster
** manager that does not yet exists. If we find one that does not
** exists we must go ahead and create it.
*/
while (set_of_application_info != NULL)
{
CAppRosterMgr *pMgr;
// First set up the session key PDU pointer
session_key = &set_of_application_info->value.session_key;
/*
** We first iterate through the complete list of application
** roster manager objects looking for one with an application key that
** matches the key in the PDU. If it is not found we create it.
*/
app_roster_manager = NULL;
new_app_roster_manager = NULL;
//
// LONCHANC: We should be able to move this as separate common subroutine.
//
m_AppRosterMgrList.Reset();
while (NULL != (pMgr = m_AppRosterMgrList.Iterate()))
{
if (pMgr->IsThisYourSessionKeyPDU(session_key))
{
// This application roster manager exist so return it.
app_roster_manager = pMgr;
break;
}
}
/*
** If a roster manager associated with this app key does not exist
** we must create it here.
*/
if (app_roster_manager == NULL)
{
DBG_SAVE_FILE_LINE
app_roster_manager = new CAppRosterMgr(
NULL,
session_key,
m_nConfID,
m_pMcsUserObject,
this,
&rc);
if (NULL == app_roster_manager || GCC_NO_ERROR != rc)
{
ERROR_OUT(("CConf::ProcessAppRosterIndicationPDU: can't create app roster mgr, rc=%d", rc));
if (NULL != app_roster_manager)
{
app_roster_manager->Release();
}
else
{
rc = GCC_ALLOCATION_FAILURE;
}
goto MyExit;
}
new_app_roster_manager = app_roster_manager;
}
/*
** We no process this set of application information. We pass it
** to the app roster manager found or created above.
*/
rc = app_roster_manager->ProcessRosterUpdateIndicationPDU(
set_of_application_info,
sender_id);
if (GCC_NO_ERROR != rc)
{
//
// LONCHANC: We should delete the newly created roster mgr.
//
if (NULL != new_app_roster_manager)
{
new_app_roster_manager->Release();
}
goto MyExit;
}
/*
** Save the new application roster manager if one was created
** when processing this roster update.
*/
if (new_app_roster_manager != NULL)
{
m_AppRosterMgrList.Append(new_app_roster_manager);
}
// Load the next application information structure.
set_of_application_info = set_of_application_info->next;
}
ASSERT(GCC_NO_ERROR == rc);
MyExit:
DebugExitINT(CConf::ProcessAppRosterIndicationPDU, rc);
return rc;
}
/*
* CConf::ProcessDetachUserIndication ()
*
* Private Function Description
* This routine sends the detach user indication to the node controler
* and updates the roster.
*
* Formal Parameters:
* detached_user - User ID of user that detached from the conference.
* reason - Reason that the user detached.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessDetachUserIndication
(
UserID detached_user,
GCCReason reason
)
{
GCCError err = GCC_NO_ERROR;
UINT cRecords;
DebugEntry(CConf::ProcessDetachUserIndication);
if (m_fConfIsEstablished)
{
CAppRosterMgr *lpAppRosterMgr;
/*
** Send a disconnect indication to the node controller if this
** detached user corresponds to a GCC user id.
*/
if (m_pConfRosterMgr->Contains(detached_user))
{
g_pControlSap->ConfDisconnectIndication(
m_nConfID,
reason,
detached_user);
}
// Here we update the CConf Roster and the Application Roster.
err = m_pConfRosterMgr->RemoveUserReference(detached_user);
if (err == GCC_NO_ERROR)
{
if (IsConfTopProvider())
{
cRecords = m_pConfRosterMgr->GetNumberOfNodeRecords();
/*
** If only one record remains in the conference roster
** it must be the local nodes record. Therefore, if
** the conference is set up to be automatically
** terminated the owner object is notified to delete
** the conference.
*/
if ((m_eTerminationMethod == GCC_AUTOMATIC_TERMINATION_METHOD)
&& (cRecords == 1))
{
TRACE_OUT(("CConf::ProcessDetachUserIndication: AUTOMATIC_TERMINATION"));
InitiateTermination(GCC_REASON_NORMAL_TERMINATION, 0);
}
// If this is the convener set its node id back to 0
if (m_nConvenerNodeID == detached_user)
{
m_nConvenerNodeID = 0;
}
}
}
else
if (err == GCC_INVALID_PARAMETER)
{
err = GCC_NO_ERROR;
}
/*
** Cleanup the Application Rosters of any records owned by this node.
*/
m_AppRosterMgrList.Reset();
while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate()))
{
err = lpAppRosterMgr->RemoveUserReference(detached_user);
if (GCC_NO_ERROR != err)
{
WARNING_OUT(("CConf::ProcessDetachUserIndication: can't remove user reference from app roster mgr, err=%d", err));
break;
}
}
// Remove ownership rights this user had on any registry entries.
m_pAppRegistry->RemoveNodeOwnership(detached_user);
// Cleanup Conductorship if detached user was the conductor
if (detached_user == m_nConductorNodeID)
{
ProcessConductorReleaseIndication(0);
}
/*
** Here we give the roster managers a chance to flush any PDUs
** or data that might have gotten queued when removing the user
** reference. An error here is considered FATAL in that the conference
** information base at this node is now corrupted therefore we
** terminate the conference.
*/
if (err == GCC_NO_ERROR)
{
//
// We just got detach user indication from the wire.
//
err = FlushRosterData();
}
if (err != GCC_NO_ERROR)
{
ERROR_OUT(("CConf::ProcessDetachUserIndication: Error occured when flushing the rosters, err=%d", err));
InitiateTermination((err == GCC_ALLOCATION_FAILURE) ?
GCC_REASON_ERROR_LOW_RESOURCES :
GCC_REASON_ERROR_TERMINATION,
0);
}
}
DebugExitVOID(CConf::ProcessDetachUserIndication);
}
/*
* CConf::ProcessTerminateRequest ()
*
* Private Function Description
* This routine processes a terminate request received from the MCSUser
* object.
*
* Formal Parameters:
* requester_id - User ID of node that is requesting the terminate.
* reason - Reason for termination.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessTerminateRequest
(
UserID requester_id,
GCCReason reason
)
{
DebugEntry(CConf::ProcessTerminateRequest);
if (DoesRequesterHavePrivilege(requester_id, TERMINATE_PRIVILEGE))
{
TRACE_OUT(("CConf::ProcessTerminateRequest: Node has permission to terminate"));
/*
** Since the terminate was successful, we go ahead and set the
** m_fConfIsEstablished instance variable to FALSE. This prevents
** any other messages from flowing to the SAPs other than terminate
** messages.
*/
m_fConfIsEstablished = FALSE;
// Send a positive response to the requesting node
m_pMcsUserObject->ConferenceTerminateResponse(requester_id, GCC_RESULT_SUCCESSFUL);
/*
** This request will kick off a terminate at this node as well as
** all the nodes below this node in the connection hierarchy.
*/
m_pMcsUserObject->ConferenceTerminateIndication(reason);
}
else
{
WARNING_OUT(("CConf::ProcessTerminateRequest: Node does NOT have permission to terminate"));
// Send a negative response to the requesting node
m_pMcsUserObject->ConferenceTerminateResponse(requester_id, GCC_RESULT_INVALID_REQUESTER);
}
DebugExitVOID(CConf::ProcessTerminateRequest);
}
/*
* CConf::ProcessTerminateIndication ()
*
* Private Function Description
* This routine takes care of both a normal termination through
* a terminate pdu and termination that occurs due to a parent
* node disconnecting.
*
* Formal Parameters:
* reason - Reason for termination.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessTerminateIndication ( GCCReason gcc_reason )
{
UserID user_id;
DebugEntry(CConf::ProcessTerminateIndication);
/*
** Setting this to true here will insure that a terminate indication
** will be delivered to the control SAP.
*/
m_fConfTerminatePending = TRUE;
if (gcc_reason == GCC_REASON_PARENT_DISCONNECTED)
{
TRACE_OUT(("CConf::ProcessTerminateIndication: Terminate due to parent disconnecting"));
user_id = m_pMcsUserObject->GetMyNodeID();
}
else
if (m_ConnHandleList.IsEmpty())
{
TRACE_OUT(("CConf: ProcessTerminateIndication: Terminate due to request (no child connections)"));
/*
** Since there is a flaw in the terminate indication PDU were the
** node id that requested the termination is not sent we always
** assume here that the request came from the top provider (which
** is only partially true).
*/
user_id = m_pMcsUserObject->GetTopNodeID();
}
else
{
TRACE_OUT(("CConf::ProcessTerminateIndication: Wait till children disconnect before terminating"));
/*
** Wait until disconnect provider indications are received on all the
** child connections before terminating the conference.
*/
m_eConfTerminateReason = gcc_reason;
DBG_SAVE_FILE_LINE
m_pConfTerminateAlarm = new Alarm (TERMINATE_TIMER_DURATION);
if (NULL != m_pConfTerminateAlarm)
{
// let's wait, bail out without initiating termination.
goto MyExit;
}
// Go ahead and terminate if there is a resource error
ERROR_OUT(("CConf: ProcessTerminateIndication: can't create terminate alarm"));
user_id = m_pMcsUserObject->GetTopNodeID();
}
InitiateTermination(gcc_reason, user_id);
MyExit:
DebugExitVOID(CConf::ProcessTerminateIndication);
}
/*
* CConf::ProcessUserIDIndication ()
*
* Private Function Description
* This routine is responsible for matching incomming user IDs with
* tag numbers returned by the subordinate node.
*
* Formal Parameters:
* tag_number - Tag used to match incomming user ID indication.
* user_id - User ID of node sending the indication.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
// checkpoint
void CConf::
ProcessUserIDIndication
(
TagNumber tag_number,
UserID user_id
)
{
INVITE_REQ_INFO *invite_request_info;
ConnectionHandle nConnHdl;
DebugEntry(CConf::ProcessUserIDIndication);
if (0 != (nConnHdl = m_ConnHdlTagNumberList2.Remove(tag_number)))
{
INVITE_REQ_INFO *lpInvReqInfo;
if (m_pMcsUserObject != NULL)
{
TRACE_OUT(("CConf: ProcessUserIDIndication: ID is set"));
m_pMcsUserObject->SetChildUserIDAndConnection(user_id, nConnHdl);
}
else
{
TRACE_OUT(("CConf::UserIDIndication: Error User Att. is NULL"));
}
/*
** Here we send an indication informing the node controller that
** a subordinate node has completed initialization.
*/
g_pControlSap->SubInitializationCompleteIndication (user_id, nConnHdl);
/*
** Now we determine if the responding node is the convener and if it
** is we will set up the m_nConvenerNodeID. This node id is used to
** determine privileges on certain GCC operations.
*/
if (m_nConvenerUserIDTagNumber == tag_number)
{
TRACE_OUT(("CConf::UserIDIndication: Convener Node ID is being set"));
m_nConvenerUserIDTagNumber = 0;
m_nConvenerNodeID = user_id;
}
/*
** If this is a User ID from an invited node we must pass the invite
** confirm to the Node Controller.
*/
m_InviteRequestList.Reset();
invite_request_info = NULL;
while (NULL != (lpInvReqInfo = m_InviteRequestList.Iterate()))
{
if (tag_number == lpInvReqInfo->invite_tag)
{
invite_request_info = lpInvReqInfo;
break;
}
}
if (invite_request_info != NULL)
{
g_pControlSap->ConfInviteConfirm(
m_nConfID,
invite_request_info->user_data_list,
GCC_RESULT_SUCCESSFUL,
invite_request_info->connection_handle);
// Free up user data if it exists
if (invite_request_info->user_data_list != NULL)
{
invite_request_info->user_data_list->Release();
}
// Cleanup the invite request list
m_InviteRequestList.Remove(invite_request_info);
// Free up the invite request info structure
delete invite_request_info;
}
}
else
{
TRACE_OUT(("CConf::ProcessUserIDIndication: Bad User ID Tag Number received"));
}
DebugExitVOID(CConf::ProcessUserIDIndication);
}
/*
* CConf::ProcessUserCreateConfirm ()
*
* Private Function Description
* This routine handles the processes that occur after a user
* create confirm is received. This process will differ depending
* on what the node type is.
*
* Formal Parameters:
* result_value - Result of the user attachment being created.
* node_id - This nodes node id.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessUserCreateConfirm
(
UserResultType result_value,
UserID node_id
)
{
GCCError err = GCC_NO_ERROR;
PUChar encoded_pdu;
UINT encoded_pdu_length;
ConnectGCCPDU connect_pdu;
MCSError mcs_error;
GCCConferenceName conference_name;
GCCNumericString conference_modifier;
GCCNumericString remote_modifier = NULL;
BOOL is_top_provider;
DebugEntry(CConf::ProcessUserCreateConfirm);
if (result_value == USER_RESULT_SUCCESSFUL)
{
switch (m_eNodeType)
{
case TOP_PROVIDER_NODE:
/*
** Encode the tag number into the ConferenceCreateResponse
** PDU. If we have gotten this far the result is success.
*/
connect_pdu.choice = CONFERENCE_CREATE_RESPONSE_CHOSEN;
connect_pdu.u.conference_create_response.bit_mask = 0;
connect_pdu.u.conference_create_response.node_id = node_id;
/*
** Here we save this particular User ID tag and mark it as the
** conveners so that when the convener's user ID is returned
** the m_nConvenerNodeID instance variable can be properly
** initialized.
*/
m_nConvenerUserIDTagNumber = GetNewUserIDTag ();
connect_pdu.u.conference_create_response.tag = m_nConvenerUserIDTagNumber;
if (m_pUserDataList != NULL)
{
connect_pdu.u.conference_create_response.bit_mask |= CCRS_USER_DATA_PRESENT;
err = m_pUserDataList->GetUserDataPDU(
&connect_pdu.u.conference_create_response.ccrs_user_data);
if (err != GCC_NO_ERROR)
{
// Terminate conference due to resource error
InitiateTermination ( GCC_REASON_ERROR_LOW_RESOURCES,
0);
break;
}
}
connect_pdu.u.conference_create_response.result =
::TranslateGCCResultToCreateResult(GCC_RESULT_SUCCESSFUL);
if (g_GCCCoder->Encode((LPVOID) &connect_pdu,
CONNECT_GCC_PDU,
PACKED_ENCODING_RULES,
&encoded_pdu,
&encoded_pdu_length))
{
mcs_error = g_pMCSIntf->ConnectProviderResponse (
m_hConvenerConnection,
&m_nConfID,
m_pDomainParameters,
RESULT_SUCCESSFUL,
encoded_pdu,
encoded_pdu_length);
g_GCCCoder->FreeEncoded(encoded_pdu);
if (mcs_error == MCS_NO_ERROR)
{
m_fConfIsEstablished = TRUE;
/*
** Add the user's tag number to the list of
** outstanding user ids along with its associated
** connection.
*/
ASSERT(0 != m_hConvenerConnection);
m_ConnHdlTagNumberList2.Append(connect_pdu.u.conference_create_response.tag,
m_hConvenerConnection);
}
else if (mcs_error == MCS_DOMAIN_PARAMETERS_UNACCEPTABLE)
{
/*
** Inform the node controller that the reason
** the conference was terminated was that the
** domain parameter passed in the Create Response
** were unacceptable.
*/
InitiateTermination(GCC_REASON_DOMAIN_PARAMETERS_UNACCEPTABLE, 0);
}
else
{
InitiateTermination(GCC_REASON_MCS_RESOURCE_FAILURE, 0);
}
}
else
{
/*
** A Fatal Resource error has occured. At this point
** the conference is invalid and should be terminated.
*/
ERROR_OUT(("CConf::ProcessUserCreateConfirm: can't encode. Terminate Conference"));
InitiateTermination(GCC_REASON_ERROR_LOW_RESOURCES, 0);
}
break;
case CONVENER_NODE:
if(g_pControlSap)
{
/*
** Send the GCC User ID is here. This will require a call to
** the User Object. The tag number that was returned in
** the ConfCreateResponse call is used here.
*/
if (m_pMcsUserObject != NULL)
{
m_pMcsUserObject->SendUserIDRequest(m_nParentIDTagNumber);
}
// Fill in the conference name data pointers.
GetConferenceNameAndModifier(&conference_name, &conference_modifier);
g_pControlSap->ConfCreateConfirm(&conference_name,
conference_modifier,
m_nConfID,
m_pDomainParameters,
m_pUserDataList,
GCC_RESULT_SUCCESSFUL,
m_hParentConnection);
// Free up the User Data List
if (m_pUserDataList != NULL)
{
m_pUserDataList->Release();
m_pUserDataList = NULL;
}
m_fConfIsEstablished = TRUE;
}
break;
case TOP_PROVIDER_AND_CONVENER_NODE:
if(g_pControlSap)
{
/*
** First set up the convener node id. In this case it is
** identical to the node ID of the Top Provider which is this
** node.
*/
m_nConvenerNodeID = m_pMcsUserObject->GetMyNodeID();
// Fill in the conference name data pointers.
GetConferenceNameAndModifier( &conference_name,
&conference_modifier);
g_pControlSap->ConfCreateConfirm(
&conference_name,
conference_modifier,
m_nConfID,
m_pDomainParameters,
NULL,
GCC_RESULT_SUCCESSFUL,
0); //Parent Connection
m_fConfIsEstablished = TRUE;
}
break;
case JOINED_NODE:
case JOINED_CONVENER_NODE:
if(g_pControlSap)
{
/*
** Send the GCC User ID is here. This will require a call to
** the User Object. The tag number that was returned in
** the ConfCreateResponse call is used here.
*/
if (m_pMcsUserObject != NULL)
{
m_pMcsUserObject->SendUserIDRequest(m_nParentIDTagNumber);
}
// Fill in the conference name data pointers.
GetConferenceNameAndModifier( &conference_name,
&conference_modifier);
if (m_pszRemoteModifier != NULL)
{
remote_modifier = (GCCNumericString) m_pszRemoteModifier;
}
g_pControlSap->ConfJoinConfirm(
&conference_name,
remote_modifier,
conference_modifier,
m_nConfID,
NULL,
m_pDomainParameters,
m_fClearPassword,
m_fConfLocked,
m_fConfListed,
m_fConfConductible,
m_eTerminationMethod,
m_pConductorPrivilegeList,
m_pConductModePrivilegeList,
m_pNonConductModePrivilegeList,
m_pwszConfDescription,
m_pUserDataList,
GCC_RESULT_SUCCESSFUL,
m_hParentConnection,
NULL,
0);
m_fConfIsEstablished = TRUE;
}
break;
case INVITED_NODE:
/*
** Send the GCC User ID here. This will require a call to
** the User Object.
*/
if (m_pMcsUserObject != NULL)
m_pMcsUserObject->SendUserIDRequest(m_nParentIDTagNumber);
m_fConfIsEstablished = TRUE;
break;
default:
TRACE_OUT(("CConf:UserCreateConfirm: Error: Bad User Type"));
break;
}
if (m_fConfIsEstablished)
{
/*
** We now instantiate the conference roster manager to be used
** with this conference.
*/
if ((m_eNodeType == TOP_PROVIDER_NODE) ||
(m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE))
{
is_top_provider = TRUE;
}
else
is_top_provider = FALSE;
DBG_SAVE_FILE_LINE
m_pConfRosterMgr = new CConfRosterMgr(
m_pMcsUserObject,
this,
is_top_provider,
&err);
if (m_pConfRosterMgr == NULL)
err = GCC_ALLOCATION_FAILURE;
/*
** We create the application registry object here because we now
** know the node type.
*/
if (err == GCC_NO_ERROR)
{
if ((m_eNodeType == TOP_PROVIDER_NODE) ||
(m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE))
{
DBG_SAVE_FILE_LINE
m_pAppRegistry = new CRegistry(
m_pMcsUserObject,
TRUE,
m_nConfID,
&m_AppRosterMgrList,
&err);
}
else
{
DBG_SAVE_FILE_LINE
m_pAppRegistry = new CRegistry(
m_pMcsUserObject,
FALSE,
m_nConfID,
&m_AppRosterMgrList,
&err);
}
}
if ((m_pAppRegistry != NULL) &&
(err == GCC_NO_ERROR))
{
/*
** Inform the node controller that it is time to do an announce
** presence for this conference.
*/
g_pControlSap->ConfPermissionToAnnounce(m_nConfID, node_id);
/*
** Make the owner callback to inform that the owner object that
** the conference object was successfully created. This also
** kicks off the permission to enroll process.
*/
g_pGCCController->ProcessConfEstablished(m_nConfID);
/*
** For all nodes except the top provider node we allocate a
** startup alarm that is used to hold back all roster flushes
** for a certain length of time giving all the local APEs
** time to enroll. An allocation failure here is not FATAL
** since everything will work with or without this alarm.
** Without the Alarm there may be a bit more network traffic
** during the startup process. Note that there is no need
** for a startup alarm if there are no application SAPs.
*/
if ((m_eNodeType != TOP_PROVIDER_NODE) &&
(m_eNodeType != TOP_PROVIDER_AND_CONVENER_NODE))
{
TRACE_OUT(("CConf:ProcessUserCreateConfirm: Creating Startup Alarm"));
// m_pConfStartupAlarm = new Alarm(STARTUP_TIMER_DURATION);
}
}
else
{
TRACE_OUT(("CConf: UserCreateConfirm: Error initializing"));
InitiateTermination(GCC_REASON_ERROR_LOW_RESOURCES, 0);
}
}
}
else
{
TRACE_OUT(("CConf: UserCreateConfirm: Create of User Att. Failed"));
/*
** Try to properly cleanup here. Since the user creation failed
** the conference is no longer valid and needs to be cleaned up.
*/
switch (m_eNodeType)
{
case TOP_PROVIDER_NODE:
g_pMCSIntf->ConnectProviderResponse (
m_hConvenerConnection,
&m_nConfID,
m_pDomainParameters,
RESULT_UNSPECIFIED_FAILURE,
NULL, 0);
break;
case CONVENER_NODE:
case TOP_PROVIDER_AND_CONVENER_NODE:
if(g_pControlSap)
{
GetConferenceNameAndModifier( &conference_name,
&conference_modifier);
g_pControlSap->ConfCreateConfirm(
&conference_name,
conference_modifier,
m_nConfID,
m_pDomainParameters,
NULL,
GCC_RESULT_RESOURCES_UNAVAILABLE,
m_hParentConnection);
}
break;
case JOINED_NODE:
case JOINED_CONVENER_NODE:
if(g_pControlSap)
{
GetConferenceNameAndModifier( &conference_name,
&conference_modifier);
if (m_pszRemoteModifier != NULL)
{
remote_modifier = (GCCNumericString) m_pszRemoteModifier;
}
g_pControlSap->ConfJoinConfirm(
&conference_name,
remote_modifier,
conference_modifier,
m_nConfID,
NULL,
m_pDomainParameters,
m_fClearPassword,
m_fConfLocked,
m_fConfListed,
m_fConfConductible,
m_eTerminationMethod,
m_pConductorPrivilegeList,
m_pConductModePrivilegeList,
m_pNonConductModePrivilegeList,
m_pwszConfDescription,
m_pUserDataList,
GCC_RESULT_RESOURCES_UNAVAILABLE,
m_hParentConnection,
NULL,
0);
}
break;
case INVITED_NODE:
default:
break;
}
/*
** A Fatal Resource error has occured. At this point
** the conference is invalid and should be terminated.
*/
InitiateTermination(GCC_REASON_MCS_RESOURCE_FAILURE, 0);
}
DebugExitVOID(CConf::ProcessUserCreateConfirm);
}
// Calls received from the MCS interface
/*
* CConf::ProcessConnectProviderConfirm ()
*
* Private Function Description
* This routine processes connect provider confirms received
* directly from MCS.
*
* Formal Parameters:
* connect_provider_confirm - This structure contains the MCS related
* data such as sender id and connection
* Handle as well as the PDU data.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConnectProviderConfirm ( PConnectProviderConfirm connect_provider_confirm )
{
PPacket packet;
PConnectGCCPDU connect_pdu;
PacketError packet_error;
GCCResult result = GCC_RESULT_SUCCESSFUL;
GCCConferenceName conference_name;
GCCNumericString conference_modifier;
GCCNumericString remote_modifier = NULL;
INVITE_REQ_INFO *invite_request_info;
DebugEntry(CConf::ProcessConnectProviderConfirm);
if (connect_provider_confirm->user_data_length != 0)
{
/*
** If the result is success create the packet to be decoded from
** the PDU passed back in the MCS user data field. If creation
** failes this again is a FATAL error and the conference must be
** terminated.
*/
DBG_SAVE_FILE_LINE
packet = new Packet((PPacketCoder) g_GCCCoder,
PACKED_ENCODING_RULES,
connect_provider_confirm->user_data,
connect_provider_confirm->user_data_length,
CONNECT_GCC_PDU,
TRUE,
&packet_error);
if ((packet != NULL) && (packet_error == PACKET_NO_ERROR))
{
connect_pdu = (PConnectGCCPDU)packet->GetDecodedData();
/*
** If all the above succeeds then decode the packet based on
** which node type this is.
*/
switch (connect_pdu->choice)
{
case CONFERENCE_CREATE_RESPONSE_CHOSEN:
ProcessConferenceCreateResponsePDU (
&connect_pdu->u.conference_create_response,
connect_provider_confirm);
break;
case CONNECT_JOIN_RESPONSE_CHOSEN:
ProcessConferenceJoinResponsePDU (
&connect_pdu->u.connect_join_response,
connect_provider_confirm);
break;
case CONFERENCE_INVITE_RESPONSE_CHOSEN:
ProcessConferenceInviteResponsePDU (
&connect_pdu->u.conference_invite_response,
connect_provider_confirm );
break;
default:
ERROR_OUT(("CConf:ProcessConnectProviderConfirm: "
"Error: Received Invalid Connect Provider Confirm"));
break;
}
// Free the decoded packet
packet->Unlock ();
}
else
{
ERROR_OUT(("CConf: ProcessConnectProviderConfirm:"
"Incompatible protocol occured"));
result = GCC_RESULT_INCOMPATIBLE_PROTOCOL;
}
}
else
{
ERROR_OUT(("CConf::ProcessConnectProviderConfirm: result=%d", (UINT) connect_provider_confirm->result));
/*
** This section of the code assumes that there is no connect PDU in
** the returned packet. First determine what the result is. We
** assume that if the MCS connection was rejected due to
** parameters being unacceptable and no GCC pdu was returned that there
** was a protocol incompatibility.
*/
if (connect_provider_confirm->result == RESULT_PARAMETERS_UNACCEPTABLE)
result = GCC_RESULT_INCOMPATIBLE_PROTOCOL;
else
{
result = ::TranslateMCSResultToGCCResult(connect_provider_confirm->result);
}
}
// Handle any errors that might have occured.
if (result != GCC_RESULT_SUCCESSFUL)
{
INVITE_REQ_INFO *lpInvReqInfo;
// First check to see if there are any outstanding invite request
m_InviteRequestList.Reset();
invite_request_info = NULL;
while (NULL != (lpInvReqInfo = m_InviteRequestList.Iterate()))
{
if (connect_provider_confirm->connection_handle == lpInvReqInfo->connection_handle)
{
TRACE_OUT(("CConf: ProcessConnectProviderConfirm: Found Invite Request Match"));
invite_request_info = lpInvReqInfo;
break;
}
}
if (invite_request_info != NULL)
{
// This must be the confirm of an invite
ProcessConferenceInviteResponsePDU (NULL, connect_provider_confirm);
}
else
{
switch (m_eNodeType)
{
case CONVENER_NODE:
case TOP_PROVIDER_AND_CONVENER_NODE:
GetConferenceNameAndModifier ( &conference_name,
&conference_modifier);
g_pControlSap->ConfCreateConfirm(
&conference_name,
conference_modifier,
m_nConfID,
m_pDomainParameters,
NULL,
result,
connect_provider_confirm->connection_handle);
InitiateTermination ( GCC_REASON_ERROR_TERMINATION,
0);
break;
case JOINED_NODE:
case JOINED_CONVENER_NODE:
TRACE_OUT(("CConf::ProcessConnectProviderConfirm:"
"Joined Node connect provider failed"));
GetConferenceNameAndModifier ( &conference_name,
&conference_modifier);
if (m_pszRemoteModifier != NULL)
{
remote_modifier = (GCCNumericString) m_pszRemoteModifier;
}
TRACE_OUT(("CConf::ProcessConnectProviderConfirm: Before conference Join Confirm"));
g_pControlSap->ConfJoinConfirm(
&conference_name,
remote_modifier,
conference_modifier,
m_nConfID,
NULL,
m_pDomainParameters,
m_fClearPassword,
m_fConfLocked,
m_fConfListed,
m_fConfConductible,
m_eTerminationMethod,
m_pConductorPrivilegeList,
m_pConductModePrivilegeList,
m_pNonConductModePrivilegeList,
NULL,
NULL,
result,
connect_provider_confirm->connection_handle,
NULL,
0);
TRACE_OUT(("CConf::ProcessConnectProviderConfirm: After conference Join Confirm"));
InitiateTermination(GCC_REASON_ERROR_TERMINATION, 0);
break;
default:
TRACE_OUT(("CConf: ProcessConnectProviderConfirm:"
"Assertion Failure: Bad confirm received"));
break;
}
}
}
DebugExitVOID(CConf::ProcessConnectProviderConfirm);
}
/*
* void ProcessConferenceCreateResponsePDU ()
*
* Private Function Description
* This routine processes a Conference Create Response PDU that is
* delivered as part of a Connect Provider Confirm.
*
* Formal Parameters:
* create_response - This is the Conference Create response
* PDU.
* connect_provider_confirm - This structure contains the MCS related
* data such as sender id and connection
* Handle as well as the PDU data.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceCreateResponsePDU
(
PConferenceCreateResponse create_response,
PConnectProviderConfirm connect_provider_confirm
)
{
GCCError err = GCC_NO_ERROR;
GCCResult result;
UserID top_gcc_node_id;
UserID parent_user_id;
GCCConferenceName conference_name;
GCCNumericString conference_modifier;
DebugEntry(CConf::ProcessConnectProviderConfirm);
// Translate the result back to GCC Result
result = ::TranslateCreateResultToGCCResult(create_response->result);
if ((result == GCC_RESULT_SUCCESSFUL) &&
(connect_provider_confirm->result == RESULT_SUCCESSFUL))
{
/*
** Save the domain parameters. The domain parameters returned in
** the connect provider confirm should always be up to date.
*/
if (m_pDomainParameters == NULL)
{
DBG_SAVE_FILE_LINE
m_pDomainParameters = new DomainParameters;
}
if (m_pDomainParameters != NULL)
*m_pDomainParameters = connect_provider_confirm->domain_parameters;
else
err = GCC_ALLOCATION_FAILURE;
// Get any user data that might exists
if ((create_response->bit_mask & CCRS_USER_DATA_PRESENT) &&
(err == GCC_NO_ERROR))
{
DBG_SAVE_FILE_LINE
m_pUserDataList = new CUserDataListContainer(create_response->ccrs_user_data, &err);
if (m_pUserDataList == NULL)
err = GCC_ALLOCATION_FAILURE;
}
if (err == GCC_NO_ERROR)
{
m_nParentIDTagNumber = create_response->tag;
top_gcc_node_id = create_response->node_id;
parent_user_id = top_gcc_node_id;
// Create the user attachment object.
DBG_SAVE_FILE_LINE
m_pMcsUserObject = new MCSUser(this, top_gcc_node_id, parent_user_id, &err);
if (m_pMcsUserObject == NULL || GCC_NO_ERROR != err)
{
if (NULL != m_pMcsUserObject)
{
m_pMcsUserObject->Release();
m_pMcsUserObject = NULL;
}
else
{
err = GCC_ALLOCATION_FAILURE;
}
}
}
}
else
{
TRACE_OUT(("CConf: ProcessConnectProviderConfirm: conference create result was Failure"));
// Go ahead and translate the mcs error to a gcc error if one occured.
if ((result == GCC_RESULT_SUCCESSFUL) &&
(connect_provider_confirm->result != RESULT_SUCCESSFUL))
{
result = ::TranslateMCSResultToGCCResult(connect_provider_confirm->result);
}
// Get the conference name to pass back in the create confirm
GetConferenceNameAndModifier ( &conference_name,
&conference_modifier);
g_pControlSap->ConfCreateConfirm(
&conference_name,
conference_modifier,
m_nConfID,
m_pDomainParameters,
NULL,
result,
connect_provider_confirm->connection_handle);
// Terminate the conference
InitiateTermination ( GCC_REASON_NORMAL_TERMINATION,
0);
}
if (err != GCC_NO_ERROR)
{
InitiateTermination ( GCC_REASON_ERROR_LOW_RESOURCES,
0);
}
DebugExitVOID(CConf::ProcessConnectProviderConfirm);
}
/*
* void ProcessConferenceJoinResponsePDU ()
*
* Private Function Description
* This routine processes a Conference Join Response PDU that is
* delivered as part of a Connect Provider Confirm.
*
* Formal Parameters:
* join_response - This is the Conference Join response
* PDU.
* connect_provider_confirm - This structure contains the MCS related
* data such as sender id and connection
* Handle as well as the PDU data.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceJoinResponsePDU
(
PConferenceJoinResponse join_response,
PConnectProviderConfirm connect_provider_confirm
)
{
GCCError err = GCC_NO_ERROR;
GCCResult result;
UserID top_gcc_node_id;
UserID parent_user_id;
CPassword *password_challenge = NULL;
CUserDataListContainer *user_data_list = NULL;
GCCConferenceName conference_name;
GCCNumericString local_modifier;
GCCNumericString remote_modifier = NULL;
DebugEntry(CConf::ProcessConferenceJoinResponsePDU);
// Translate the result back to GCC Result
result = ::TranslateJoinResultToGCCResult (join_response->result);
if ((result == GCC_RESULT_SUCCESSFUL) &&
(connect_provider_confirm->result == RESULT_SUCCESSFUL))
{
/*
** Save the domain parameters. The domain parameters returned in
** the connect provider confirm should always be up to date.
*/
if (m_pDomainParameters == NULL)
{
DBG_SAVE_FILE_LINE
m_pDomainParameters = new DomainParameters;
}
if (m_pDomainParameters != NULL)
*m_pDomainParameters = connect_provider_confirm->domain_parameters;
else
err = GCC_ALLOCATION_FAILURE;
// Get the conference name alias if one exists
if ((join_response->bit_mask & CONFERENCE_NAME_ALIAS_PRESENT) &&
(err == GCC_NO_ERROR))
{
if (join_response->conference_name_alias.choice ==
NAME_SELECTOR_NUMERIC_CHOSEN)
{
delete m_pszConfNumericName;
if (NULL == (m_pszConfNumericName = ::My_strdupA(
join_response->conference_name_alias.u.name_selector_numeric)))
{
err = GCC_ALLOCATION_FAILURE;
}
}
else
{
delete m_pwszConfTextName;
if (NULL == (m_pwszConfTextName = ::My_strdupW2(
join_response->conference_name_alias.u.name_selector_text.length,
join_response->conference_name_alias.u.name_selector_text.value)))
{
err = GCC_ALLOCATION_FAILURE;
}
}
}
// Get the conductor privilege list if one exists
if ((join_response->bit_mask & CJRS_CONDUCTOR_PRIVS_PRESENT) &&
(err == GCC_NO_ERROR))
{
delete m_pConductorPrivilegeList;
DBG_SAVE_FILE_LINE
m_pConductorPrivilegeList = new PrivilegeListData(join_response->cjrs_conductor_privs);
if (m_pConductorPrivilegeList == NULL)
err = GCC_ALLOCATION_FAILURE;
}
// Get the conducted mode privilege list if one exists
if ((join_response->bit_mask & CJRS_CONDUCTED_PRIVS_PRESENT) &&
(err == GCC_NO_ERROR))
{
delete m_pConductModePrivilegeList;
DBG_SAVE_FILE_LINE
m_pConductModePrivilegeList = new PrivilegeListData(join_response->cjrs_conducted_privs);
if (m_pConductModePrivilegeList == NULL)
err = GCC_ALLOCATION_FAILURE;
}
// Get the non-conducted mode privilege list if one exists
if ((join_response->bit_mask & CJRS_NON_CONDUCTED_PRIVS_PRESENT) &&
(err == GCC_NO_ERROR))
{
delete m_pNonConductModePrivilegeList;
DBG_SAVE_FILE_LINE
m_pNonConductModePrivilegeList = new PrivilegeListData(join_response->cjrs_non_conducted_privs);
if (m_pNonConductModePrivilegeList == NULL)
err = GCC_ALLOCATION_FAILURE;
}
// Get the conference description if it exists
if ((join_response->bit_mask & CJRS_DESCRIPTION_PRESENT) &&
(err == GCC_NO_ERROR))
{
delete m_pwszConfDescription;
if (NULL == (m_pwszConfDescription = ::My_strdupW2(
join_response->cjrs_description.length,
join_response->cjrs_description.value)))
{
err = GCC_ALLOCATION_FAILURE;
}
}
// Get the user data if it exists
if ((join_response->bit_mask & CJRS_USER_DATA_PRESENT) &&
(err == GCC_NO_ERROR))
{
if (NULL != m_pUserDataList)
{
m_pUserDataList->Release();
}
DBG_SAVE_FILE_LINE
m_pUserDataList = new CUserDataListContainer(join_response->cjrs_user_data, &err);
// in case of err but valid m_pUserDataList, the destructor will clean it up.
if (m_pUserDataList == NULL)
{
err = GCC_ALLOCATION_FAILURE;
}
}
if (err == GCC_NO_ERROR)
{
parent_user_id = (join_response->bit_mask & CJRS_NODE_ID_PRESENT) ?
(UserID) join_response->cjrs_node_id :
(UserID) join_response->top_node_id;
m_nParentIDTagNumber = join_response->tag;
top_gcc_node_id = (UserID)join_response->top_node_id;
m_fClearPassword = join_response->clear_password_required;
m_fConfLocked = join_response->conference_is_locked;
m_fConfListed = join_response->conference_is_listed;
m_eTerminationMethod = (GCCTerminationMethod)join_response->termination_method;
m_fConfConductible = join_response->conference_is_conductible;
// Create the user attachment object.
ASSERT(NULL == m_pMcsUserObject);
DBG_SAVE_FILE_LINE
m_pMcsUserObject = new MCSUser(this, top_gcc_node_id, parent_user_id, &err);
// in case of err but valid m_pMcsUserObject, the destructor will clean it up.
if (m_pMcsUserObject == NULL)
{
err = GCC_ALLOCATION_FAILURE;
}
}
}
else
{
if ((join_response->bit_mask & CJRS_PASSWORD_PRESENT) &&
(err == GCC_NO_ERROR))
{
DBG_SAVE_FILE_LINE
password_challenge = new CPassword(&join_response->cjrs_password, &err);
if (password_challenge == NULL)
{
err = GCC_ALLOCATION_FAILURE;
}
}
// Get the user data if it exists
if ((join_response->bit_mask & CJRS_USER_DATA_PRESENT) &&
(err == GCC_NO_ERROR))
{
DBG_SAVE_FILE_LINE
user_data_list = new CUserDataListContainer(join_response->cjrs_user_data, &err);
if (user_data_list == NULL)
{
err = GCC_ALLOCATION_FAILURE;
}
}
if (err == GCC_NO_ERROR)
{
/*
** Go ahead and translate the mcs error to a gcc error if
** one occured.
*/
if ((result == GCC_RESULT_SUCCESSFUL) &&
(connect_provider_confirm->result != RESULT_SUCCESSFUL))
{
result = ::TranslateMCSResultToGCCResult(connect_provider_confirm->result);
}
// Fill in the conference name data pointers.
GetConferenceNameAndModifier(&conference_name, &local_modifier);
if (m_pszRemoteModifier != NULL)
{
remote_modifier = (GCCNumericString) m_pszRemoteModifier;
}
//
// LONCHANC: To get rid of the conference object
// in GCC Controller's active conference list.
// The conference object will then be moved to
// the deletion list.
//
InitiateTermination ( GCC_REASON_NORMAL_TERMINATION, 0);
g_pControlSap->ConfJoinConfirm(
&conference_name,
remote_modifier,
local_modifier,
m_nConfID,
password_challenge,
m_pDomainParameters,
m_fClearPassword,
m_fConfLocked,
m_fConfListed,
m_fConfConductible,
m_eTerminationMethod,
m_pConductorPrivilegeList,
m_pConductModePrivilegeList,
m_pNonConductModePrivilegeList,
NULL,
user_data_list,
result,
connect_provider_confirm->connection_handle,
connect_provider_confirm->pb_cred,
connect_provider_confirm->cb_cred);
}
if (password_challenge != NULL)
{
password_challenge->Release();
}
if (user_data_list != NULL)
{
user_data_list->Release();
}
}
if (err != GCC_NO_ERROR)
{
InitiateTermination (GCC_REASON_ERROR_LOW_RESOURCES, 0);
}
DebugExitVOID(CConf::ProcessConferenceJoinResponsePDU);
}
/*
* void ProcessConferenceInviteResponsePDU ()
*
* Private Function Description
* This routine processes a Conference Invite Response PDU that is
* delivered as part of a Connect Provider Confirm.
*
* Formal Parameters:
* invite_response - This is the Conference Invite response
* PDU.
* connect_provider_confirm - This structure contains the MCS related
* data such as sender id and connection
* Handle as well as the PDU data.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceInviteResponsePDU
(
PConferenceInviteResponse invite_response,
PConnectProviderConfirm connect_provider_confirm
)
{
GCCError err;
GCCResult result;
CUserDataListContainer *user_data_list = NULL;
INVITE_REQ_INFO *invite_request_info = NULL;
INVITE_REQ_INFO *lpInvReqInfo;
DebugEntry(CConf::ProcessConferenceInviteResponsePDU);
// First obtain the info request info structure.
m_InviteRequestList.Reset();
while (NULL != (lpInvReqInfo = m_InviteRequestList.Iterate()))
{
if (connect_provider_confirm->connection_handle == lpInvReqInfo->connection_handle)
{
invite_request_info = lpInvReqInfo;
break;
}
}
if (invite_request_info == NULL)
return;
if (invite_response != NULL)
{
// Get the user data list if one exists
if (invite_response->bit_mask & CIRS_USER_DATA_PRESENT)
{
DBG_SAVE_FILE_LINE
user_data_list = new CUserDataListContainer(invite_response->cirs_user_data, &err);
}
// Translate the result to GCCResult
result = ::TranslateInviteResultToGCCResult(invite_response->result);
}
else
{
result = (connect_provider_confirm->result == RESULT_USER_REJECTED) ?
GCC_RESULT_INCOMPATIBLE_PROTOCOL :
::TranslateMCSResultToGCCResult(connect_provider_confirm->result);
}
if ((result == GCC_RESULT_SUCCESSFUL) &&
(connect_provider_confirm->result == RESULT_SUCCESSFUL))
{
TRACE_OUT(("CConf::ProcessConferenceInviteResponsePDU:"
"Received Connect Provider confirm on Invite"));
/*
** Save the domain parameters. The domain parameters returned in
** the connect provider confirm should always be up to date.
*/
if (m_pDomainParameters == NULL)
{
DBG_SAVE_FILE_LINE
m_pDomainParameters = new DomainParameters;
}
if (m_pDomainParameters != NULL)
*m_pDomainParameters = connect_provider_confirm->domain_parameters;
else
err = GCC_ALLOCATION_FAILURE;
// Save the user data list for the invite confirm
invite_request_info->user_data_list = user_data_list;
// Wait for user ID from invited node before sending invite confirm.
}
else
{
/*
** Go ahead and translate the mcs error to a gcc error if
** one occured.
*/
if ((result == GCC_RESULT_SUCCESSFUL) &&
(connect_provider_confirm->result != RESULT_SUCCESSFUL))
{
result = ::TranslateMCSResultToGCCResult(connect_provider_confirm->result);
}
// Cleanup the connection handle list
ASSERT(0 != connect_provider_confirm->connection_handle);
m_ConnHandleList.Remove(connect_provider_confirm->connection_handle);
// In case of error, the node controller will delete this conference.
// AddRef here to protect itself from going away.
AddRef();
g_pControlSap->ConfInviteConfirm(
m_nConfID,
user_data_list,
result,
connect_provider_confirm->connection_handle);
// Free up the user data
if (user_data_list != NULL)
{
user_data_list->Release();
}
// The reason that we check this is because in some cases, in the call to
// g_pControlSap->ConfInviteConfirm, someone was calling DeleteOutstandingInviteRequests
// which was killing the list via a call to m_InviteRequestList.Clear...
// This happens when the calee refuses to accept the call
if(m_InviteRequestList.Remove(invite_request_info))
{
// Free up the invite request info structure
delete invite_request_info;
}
// To match AddRef above.
Release();
}
DebugExitVOID(CConf::ProcessConferenceInviteResponsePDU);
}
/*
* CConf::ProcessEjectUserIndication ()
*
* Private Function Description
* This routine processes an Eject User Indication.
*
* Formal Parameters:
* reason - Reason that this node is being ejected.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessEjectUserIndication ( GCCReason reason )
{
DebugEntry(CConf::ProcessEjectUserIndication);
if (m_fConfIsEstablished)
{
/*
** First inform the control SAP that this node has been ejected from this
** particular conference.
*/
g_pControlSap->ConfEjectUserIndication(
m_nConfID,
reason,
m_pMcsUserObject->GetMyNodeID());
/*
** Next we set conference established to FALSE since the conference is
** no longer established (this also prevents a terminate indication from
** being sent).
*/
m_fConfIsEstablished = FALSE;
InitiateTermination(reason, m_pMcsUserObject->GetMyNodeID());
}
DebugExitVOID(CConf::ProcessEjectUserIndication);
}
/*
* CConf::ProcessEjectUserRequest ()
*
* Private Function Description
* This routine processes an eject user request PDU. This routine should
* only be called from the Top Provider.
*
* Formal Parameters:
* eject_node_request - This is the PDU data associated with the
* eject user request.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessEjectUserRequest ( PUserEjectNodeRequestInfo eject_node_request )
{
GCCResult result;
DebugEntry(CConf::ProcessEjectUserRequest);
// Check to make sure that the requesting node has the proper privileges
if (DoesRequesterHavePrivilege( eject_node_request->requester_id,
EJECT_USER_PRIVILEGE))
{
/*
** The user attachment object decides where the eject should
** be sent (either to the Top Provider or conference wide as
** an indication.
*/
m_pMcsUserObject->EjectNodeFromConference (
eject_node_request->node_to_eject,
eject_node_request->reason);
result = GCC_RESULT_SUCCESSFUL;
}
else
result = GCC_RESULT_INVALID_REQUESTER;
m_pMcsUserObject->SendEjectNodeResponse (eject_node_request->requester_id,
eject_node_request->node_to_eject,
result);
DebugExitVOID(CConf::ProcessEjectUserRequest);
}
/*
* CConf::ProcessEjectUserResponse ()
*
* Private Function Description
* This routine processes an eject user response PDU. This routine is
* called in response to an eject user request.
*
* Formal Parameters:
* eject_node_response - This is the PDU data associated with the
* eject user response.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessEjectUserResponse ( PUserEjectNodeResponseInfo eject_node_response )
{
DebugEntry(CConf::ProcessEjectUserResponse);
if (m_EjectedNodeConfirmList.Remove(eject_node_response->node_to_eject))
{
#ifdef JASPER
g_pControlSap->ConfEjectUserConfirm(
m_nConfID,
eject_node_response->node_to_eject,
eject_node_response->result);
#endif // JASPER
}
else
{
ERROR_OUT(("CConf::ProcessEjectUserResponse: Assertion: Bad ejected node response received"));
}
DebugExitVOID(CConf::ProcessEjectUserResponse);
}
/*
* CConf::ProcessConferenceLockRequest()
*
* Private Function Description
* This routine processes a conference lock request PDU.
*
* Formal Parameters:
* requester_id - Node ID of node making the lock request.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceLockRequest ( UserID requester_id )
{
DebugEntry(CConf::ProcessConferenceLockRequest);
if (DoesRequesterHavePrivilege (requester_id,
LOCK_UNLOCK_PRIVILEGE))
{
g_pControlSap->ConfLockIndication(m_nConfID, requester_id);
}
else
{
if (requester_id == m_pMcsUserObject->GetTopNodeID())
{
#ifdef JASPER
g_pControlSap->ConfLockConfirm(GCC_RESULT_INVALID_REQUESTER, m_nConfID);
#endif // JASPER
}
else
{
m_pMcsUserObject->SendConferenceLockResponse(
requester_id,
GCC_RESULT_INVALID_REQUESTER);
}
}
DebugExitVOID(CConf::ProcessConferenceLockRequest);
}
/*
* CConf::ProcessConferenceUnlockRequest()
*
* Private Function Description
* This routine processes a conference unlock request PDU.
*
* Formal Parameters:
* requester_id - Node ID of node making the unlock request.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceUnlockRequest ( UserID requester_id )
{
DebugEntry(CConf::ProcessConferenceUnlockRequest);
if (DoesRequesterHavePrivilege (requester_id,
LOCK_UNLOCK_PRIVILEGE))
{
#ifdef JASPER
g_pControlSap->ConfUnlockIndication(m_nConfID, requester_id);
#endif // JASPER
}
else
{
if (requester_id == m_pMcsUserObject->GetTopNodeID())
{
#ifdef JASPER
g_pControlSap->ConfUnlockConfirm(GCC_RESULT_INVALID_REQUESTER, m_nConfID);
#endif // JASPER
}
else
{
m_pMcsUserObject->SendConferenceUnlockResponse(
requester_id,
GCC_RESULT_INVALID_REQUESTER);
}
}
DebugExitVOID(CConf::ProcessConferenceUnlockRequest);
}
/*
* CConf::ProcessConferenceLockIndication()
*
* Private Function Description
* This routine processes a conference lock indication PDU.
*
* Formal Parameters:
* source_id - Node ID which sent out the lock indication. Should
* only be sent by the top provider.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceLockIndication ( UserID source_id )
{
DebugEntry(CConf::ProcessConferenceLockIndication);
if (source_id == m_pMcsUserObject->GetTopNodeID())
{
m_fConfLocked = CONFERENCE_IS_LOCKED;
#ifdef JASPER
g_pControlSap->ConfLockReport(m_nConfID, m_fConfLocked);
#endif // JASPER
}
DebugExitVOID(CConf::ProcessConferenceLockIndication);
}
/*
* CConf::ProcessConferenceUnlockIndication()
*
* Private Function Description
* This routine processes a conference unlock indication PDU.
*
* Formal Parameters:
* source_id - Node ID which sent out the unlock indication. Should
* only be sent by the top provider.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceUnlockIndication ( UserID source_id )
{
DebugEntry(CConf::ProcessConferenceUnlockIndication);
if (source_id == m_pMcsUserObject->GetTopNodeID())
{
m_fConfLocked = CONFERENCE_IS_NOT_LOCKED;
#ifdef JASPER
g_pControlSap->ConfLockReport(m_nConfID, m_fConfLocked);
#endif // JASPER
}
DebugExitVOID(CConf::ProcessConferenceUnlockIndication);
}
/*
* void ProcessConferenceTransferRequest ()
*
* Public Function Description
* This routine processes a conference transfer request PDU.
*
* Formal Parameters:
* requesting_node_id - Node ID that made the transfer
* request.
* destination_conference_name - The name of the conference to
* transfer to.
* destination_conference_modifier - The name of the conference modifier
* to transfer to.
* destination_address_list - Network address list of the
* conference to transfer to.
* number_of_destination_nodes - The number of nodes in the list of
* nodes that should perform the
* transfer.
* destination_node_list - The list of nodes that should
* perform the transfer.
* password - The password needed to join the
* new conference.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceTransferRequest
(
UserID requesting_node_id,
PGCCConferenceName destination_conference_name,
GCCNumericString destination_conference_modifier,
CNetAddrListContainer *destination_address_list,
UINT number_of_destination_nodes,
PUserID destination_node_list,
CPassword *password
)
{
GCCResult result;
DebugEntry(CConf::ProcessConferenceTransferRequest);
if (DoesRequesterHavePrivilege( requesting_node_id,
TRANSFER_PRIVILEGE))
{
result = GCC_RESULT_SUCCESSFUL;
}
else
result = GCC_RESULT_INVALID_REQUESTER;
m_pMcsUserObject->ConferenceTransferResponse (
requesting_node_id,
destination_conference_name,
destination_conference_modifier,
number_of_destination_nodes,
destination_node_list,
result);
if (result == GCC_RESULT_SUCCESSFUL)
{
m_pMcsUserObject->ConferenceTransferIndication (
destination_conference_name,
destination_conference_modifier,
destination_address_list,
number_of_destination_nodes,
destination_node_list,
password);
}
DebugExitVOID(CConf::ProcessConferenceTransferRequest);
}
/*
* CConf::ProcessConferenceAddRequest ()
*
* Private Function Description
* This routine processes a conference add request PDU.
*
* Formal Parameters:
* requesting_node_id - Node ID that made the transfer
* request.
* destination_conference_name - The name of the conference to
* transfer to.
* destination_conference_modifier - The name of the conference modifier
* to transfer to.
* destination_address_list - Network address list of the
* conference to transfer to.
* number_of_destination_nodes - The number of nodes in the list of
* nodes that should perform the
* transfer.
* destination_node_list - The list of nodes that should
* perform the transfer.
* password - The password needed to join the
* new conference.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConferenceAddRequest
(
CNetAddrListContainer *network_address_list,
CUserDataListContainer *user_data_list,
UserID adding_node,
TagNumber add_request_tag,
UserID requesting_node
)
{
BOOL generate_add_indication = FALSE;
GCCResponseTag add_response_tag;
DebugEntry(CConf::ProcessConferenceAddRequest);
if (m_pMcsUserObject->GetMyNodeID() == m_pMcsUserObject->GetTopNodeID())
{
if (DoesRequesterHavePrivilege(requesting_node, ADD_PRIVILEGE))
{
if ((m_pMcsUserObject->GetMyNodeID() == adding_node) ||
(adding_node == 0))
{
generate_add_indication = TRUE;
}
else
{
/*
** Here we send the add request on to the MCU that is
** supposed to do the adding.
*/
m_pMcsUserObject->ConferenceAddRequest(
add_request_tag,
requesting_node,
adding_node,
adding_node,
network_address_list,
user_data_list);
}
}
else
{
// Send back negative response stating inproper privileges
m_pMcsUserObject->ConferenceAddResponse(
add_request_tag,
requesting_node,
NULL,
GCC_RESULT_INVALID_REQUESTER);
}
}
else if (m_pMcsUserObject->GetMyNodeID() == adding_node)
{
/*
** This is the node that is supposed to get the add indication
** so send it on.
*/
generate_add_indication = TRUE;
}
if (generate_add_indication)
{
// First set up the Add Response Tag
while (1)
{
add_response_tag = m_nConfAddResponseTag++;
if (0 == m_AddResponseList.Find(add_response_tag))
break;
}
m_AddResponseList.Append(add_response_tag, add_request_tag);
g_pControlSap->ConfAddIndication(m_nConfID,
add_response_tag,
network_address_list,
user_data_list,
requesting_node);
}
DebugExitVOID(CConf::ProcessConferenceAddRequest);
}
/***************Conductorship Callbacks from User object*******************/
/*
* void ProcessConductorGrabConfirm ()
*
* Private Function Description
* The routine processes a conductor grab confirm received from the
* MCSUser object.
*
* Formal Parameters:
* result - This is the result from the grab request.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorGrabConfirm ( GCCResult result )
{
DebugEntry(CConf::ProcessConductorGrabConfirm);
TRACE_OUT(("CConf::ProcessConductorGrabConfirm: result = %d", result));
if ((m_eNodeType == TOP_PROVIDER_NODE) ||
(m_eNodeType == TOP_PROVIDER_AND_CONVENER_NODE))
{
#ifdef JASPER
// Inform the control SAP of the result
g_pControlSap->ConductorAssignConfirm ( result,
m_nConfID);
#endif // JASPER
/*
** If we were successful, we must send a Conductor Assign Indication
** PDU to every node in the conference to inform them that the
** conductor has changed.
*/
if (result == GCC_RESULT_SUCCESSFUL)
{
/*
** We use NULL for the conductor ID because the conductor can be
** determined from the sender of the Assign Indication PDU.
*/
m_pMcsUserObject->SendConductorAssignIndication(
m_pMcsUserObject->GetTopNodeID());
m_nConductorNodeID = m_pMcsUserObject->GetMyNodeID();
m_fConductorGrantedPermission = TRUE;
}
// Reset the Assign Request Pending flag back to FALSE.
m_fConductorAssignRequestPending = FALSE;
}
else
{
if (result == GCC_RESULT_SUCCESSFUL)
{
/*
** If this node is not the Top Provider, we must try to Give the
** Conductor token to the Top Provider. The Top Provider is used to
** monitor the use of the conductor token. I the give to the Top
** Provider is unsuccessful then this node is the new conductor.
*/
m_pMcsUserObject->ConductorTokenGive(m_pMcsUserObject->GetTopNodeID());
}
else
{
#ifdef JASPER
// Inform the control SAP of the result
g_pControlSap->ConductorAssignConfirm(result, m_nConfID);
#endif // JASPER
}
}
DebugExitVOID(CConf::ProcessConductorGrabConfirm);
}
/*
* void ProcessConductorAssignIndication ()
*
* Private Function Description
* This routine processes a conductor assign indication received from
* the MCSUser object.
*
* Formal Parameters:
* new_conductor_id - This is the node id of the new conductor.
* sender_id - Node ID of node that sent the indication.
* Should be the Top Provider.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorAssignIndication
(
UserID new_conductor_id,
UserID sender_id
)
{
DebugEntry(CConf::ProcessConductorAssignIndication);
if (sender_id == m_pMcsUserObject->GetTopNodeID())
{
TRACE_OUT(("CConf: ConductAssignInd: Received from top provider"));
// Ignore this indication if the conference is not conductible
if (m_fConfConductible)
{
// Save UserID of the new conductor if not the Top Provider
if (sender_id != m_pMcsUserObject->GetMyNodeID())
{
m_nConductorNodeID = new_conductor_id;
}
/*
** Inform the control SAP and all the enrolled application SAPs
** that there is a new conductor.
*/
TRACE_OUT(("CConf: ConductAssignInd: Send to Control SAP"));
g_pControlSap->ConductorAssignIndication(m_nConductorNodeID, m_nConfID);
/*
** We iterate on a temporary list to avoid any problems
** if the application sap leaves during the callback.
*/
CAppSap *pAppSap;
CAppSapList TempList(m_RegisteredAppSapList);
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
if (DoesSAPHaveEnrolledAPE(pAppSap))
{
pAppSap->ConductorAssignIndication(m_nConductorNodeID, m_nConfID);
}
}
}
else
{
ERROR_OUT(("CConf:ProcessConductorAssignInd: Conductor Assign sent in non-conductible conference"));
}
}
else
{
ERROR_OUT(("CConf:ProcessConductorAssignInd: Conductor Assign sent from NON-Top Provider"));
}
DebugExitVOID(CConf::ProcessConductorAssignIndication);
}
/*
* void ProcessConductorReleaseIndication ()
*
* Private Function Description
* This routine processes a conductor release indication received from
* the MCSUser object.
*
* Formal Parameters:
* sender_id - Node ID of node that sent the indication.
* Should be the Top Provider or the conductor.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorReleaseIndication ( UserID sender_id )
{
DebugEntry(CConf::ProcessConductorReleaseIndication);
if ((sender_id == m_pMcsUserObject->GetTopNodeID()) ||
(sender_id == m_nConductorNodeID) ||
(sender_id == 0))
{
// Ignore this indication if the conference is not conductible
if (m_fConfConductible)
{
m_fConductorGrantedPermission = FALSE;
// Reset to Non-Conducted mode
m_nConductorNodeID = 0;
/*
** Inform the control SAP and all the enrolled application SAPs
** that the conductor was released.
*/
g_pControlSap->ConductorReleaseIndication( m_nConfID );
/*
** We iterate on a temporary list to avoid any problems
** if the application sap leaves during the callback.
*/
CAppSap *pAppSap;
CAppSapList TempList(m_RegisteredAppSapList);
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
if (DoesSAPHaveEnrolledAPE(pAppSap))
{
pAppSap->ConductorReleaseIndication(m_nConfID);
}
}
}
}
DebugExitVOID(CConf::ProcessConductorReleaseIndication);
}
/*
* void ProcessConductorGiveIndication ()
*
* Private Function Description
* This routine processes a conductor give indication received from
* the MCSUser object.
*
* Formal Parameters:
* giving_node_id - Node ID of node that is givving up
* conductorship.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorGiveIndication ( UserID giving_node_id )
{
DebugEntry(CConf::ProcessConductorGiveIndication);
// Ignore this indication if the conference is not conductible
if (m_fConfConductible)
{
/*
** If this node is the Top Provider and node giving conductor ship is
** not the current Conductor, this node must check to make sure that
** it is valid for this node to become the Top Conductor. Otherwise,
** we can assume this is a real give.
*/
if ((giving_node_id == m_nConductorNodeID) ||
(m_pMcsUserObject->GetMyNodeID() != m_pMcsUserObject->GetTopNodeID()))
{
// This flag is set when there is an outstanding give.
m_fConductorGiveResponsePending = TRUE;
/*
** Inform the control SAP.
*/
g_pControlSap->ConductorGiveIndication(m_nConfID);
}
else
{
TRACE_OUT(("CConf: ProcessConductorGiveInd: Send REAL Assign Ind"));
m_nConductorNodeID = giving_node_id;
m_pMcsUserObject->SendConductorAssignIndication(m_nConductorNodeID);
m_pMcsUserObject->ConductorTokenGiveResponse(RESULT_USER_REJECTED);
}
}
DebugExitVOID(CConf::ProcessConductorGiveIndication);
}
/*
* void ProcessConductorGiveConfirm ()
*
* Private Function Description
* This routine processes a conductor give confirm received from
* the MCSUser object.
*
* Formal Parameters:
* result - This is the result of the give request.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorGiveConfirm ( GCCResult result )
{
DebugEntry(CConf::ProcessConductorGiveConfirm);
TRACE_OUT(("CConf::ProcessConductorGiveConfirm: result = %d", result));
// Ignore this indication if the conference is not conductible
if (m_fConfConductible)
{
/*
** First we must determine if this Give Confirm is from
** a Give Request to the Top Provider that was associated with an
** Assign Request. This type of Give Confirm is from the Top Provider.
** If not, we check to make sure that this is a Give Confirm associated
** with a give request issued by the Node Controller. Otherwise, we
** dont process it.
*/
if (m_fConductorAssignRequestPending)
{
#ifdef JASPER
/*
** The proper result is for the Top Provider to reject the give
** to the Donor User ID that is the new Conductor. This is
** straight out of the T.124 document.
*/
if (result != GCC_RESULT_SUCCESSFUL)
result = GCC_RESULT_SUCCESSFUL;
else
result = GCC_RESULT_UNSPECIFIED_FAILURE;
// Inform the control SAP of the result
g_pControlSap->ConductorAssignConfirm(result, m_nConfID);
#endif // JASPER
m_fConductorAssignRequestPending = FALSE;
}
else if (m_nPendingConductorNodeID != 0)
{
if (result == GCC_RESULT_SUCCESSFUL)
m_fConductorGrantedPermission = FALSE;
#ifdef JASPER
g_pControlSap->ConductorGiveConfirm(result, m_nConfID, m_nPendingConductorNodeID);
#endif // JASPER
// Set the pending conductor node ID back to zero.
m_nPendingConductorNodeID = 0;
}
}
DebugExitVOID(CConf::ProcessConductorGiveConfirm);
}
/*
* void ProcessConductorPermitGrantInd ()
*
* Private Function Description
* This routine processes a conductor permission grant indication received
* from the MCSUser object.
*
* Formal Parameters:
* permission_grant_indication - This is the PDU data structure
* associated with the conductor
* permission grant indication.
* sender_id - This is the node ID of the node
* that sent the indication.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorPermitGrantInd
(
PUserPermissionGrantIndicationInfo permission_grant_indication,
UserID sender_id
)
{
UINT i;
DebugEntry(CConf::ProcessConductorPermitGrantInd);
if (m_fConfConductible)
{
if (sender_id == m_nConductorNodeID)
{
// First check to see if we have been given permission
m_fConductorGrantedPermission = FALSE;
for (i = 0; i < permission_grant_indication->number_granted; i++)
{
if (permission_grant_indication->granted_node_list[i] ==
m_pMcsUserObject->GetMyNodeID())
{
TRACE_OUT(("CConf::ProcessConductorPermitGrantInd: Permission was Granted"));
m_fConductorGrantedPermission = TRUE;
break;
}
}
/*
** This indication goes to the control SAP and all the application
** SAPs.
*/
g_pControlSap->ConductorPermitGrantIndication (
m_nConfID,
permission_grant_indication->number_granted,
permission_grant_indication->granted_node_list,
permission_grant_indication->number_waiting,
permission_grant_indication->waiting_node_list,
m_fConductorGrantedPermission);
/*
** We iterate on a temporary list to avoid any problems
** if the application sap leaves during the callback.
*/
CAppSap *pAppSap;
CAppSapList TempList(m_RegisteredAppSapList);
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
if (DoesSAPHaveEnrolledAPE(pAppSap))
{
pAppSap->ConductorPermitGrantIndication(
m_nConfID,
permission_grant_indication->number_granted,
permission_grant_indication->granted_node_list,
permission_grant_indication->number_waiting,
permission_grant_indication->waiting_node_list,
m_fConductorGrantedPermission);
}
}
}
}
DebugExitVOID(CConf::ProcessConductorPermitGrantInd);
}
/*
* void ProcessConductorTestConfirm ()
*
* Private Function Description
* This routine processes a conductor test confirm received
* from the MCSUser object.
*
* Formal Parameters:
* result - This is the result of the conductor test request
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
ProcessConductorTestConfirm ( GCCResult result )
{
BOOL conducted_mode;
CBaseSap *pSap;
DebugEntry(CConf::ProcessConductorTestConfirm);
if (! m_ConductorTestList.IsEmpty())
{
if (result == GCC_RESULT_SUCCESSFUL)
conducted_mode = TRUE;
else
conducted_mode = FALSE;
/*
** Pop the next command target of the list of command targets.
** Note that all token test request are processed in the order
** that they were issued so we are gauranteed to send the confirms
** to the correct target.
*/
pSap = m_ConductorTestList.Get();
pSap->ConductorInquireConfirm(m_nConductorNodeID,
result,
m_fConductorGrantedPermission,
conducted_mode,
m_nConfID);
}
DebugExitVOID(CConf::ProcessConductorTestConfirm);
}
/*************************************************************************/
/*
* CConf::InitiateTermination ()
*
* Private Function Description
* This routine informs the owner object that the conference has
* self terminated. It also directs a disconnect provider request at
* the parent connection.
*
* Formal Parameters:
* reason - This is the reason for the termination.
* requesting_node_id - This is the node ID of the node that is
* making the request,
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
InitiateTermination
(
GCCReason reason,
UserID requesting_node_id
)
{
DebugEntry(CConf::InitiateTermination);
if (! m_fTerminationInitiated)
{
m_fTerminationInitiated = TRUE;
if (m_fConfIsEstablished ||
(reason == GCC_REASON_DOMAIN_PARAMETERS_UNACCEPTABLE) ||
m_fConfTerminatePending)
{
g_pControlSap->ConfTerminateIndication(m_nConfID, requesting_node_id, reason);
m_fConfIsEstablished = FALSE;
}
// Disconnect from the MCS parent connection if it exists
if (m_hParentConnection != NULL)
{
g_pMCSIntf->DisconnectProviderRequest(m_hParentConnection);
m_hParentConnection = NULL;
}
g_pGCCController->ProcessConfTerminated(m_nConfID, reason);
/*
** Here we cleanup the registered application list. If any Application
** SAPs are still registered we will first send them PermitToEnroll
** indications revoking the permission to enroll and then we will
** unregister them (the unregister call takes care of this). First set up
** a temporary list of the registered applications to iterate on since
** members of this list will be removed during this process.
*/
if (! m_RegisteredAppSapList.IsEmpty())
{
CAppSapList TempList(m_RegisteredAppSapList);
CAppSap *pAppSap;
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
UnRegisterAppSap(pAppSap);
}
}
}
DebugExitVOID(CConf::InitiateTermination);
}
/*
* CConf::GetConferenceNameAndModifier ()
*
* Private Function Description
* This routine returns pointers to the conference name and modifier.
*
* Formal Parameters:
* conference_name - Pointer to structure that holds the conference
* name.
* requesting_node_id - This is a pointer to a pointer that holds the
* conference modifier.
*
* Return Value
* None.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
void CConf::
GetConferenceNameAndModifier
(
PGCCConferenceName pConfName,
PGCCNumericString ppszConfModifier
)
{
pConfName->numeric_string = m_pszConfNumericName;
pConfName->text_string = m_pwszConfTextName;
*ppszConfModifier = (GCCNumericString) m_pszConfModifier;
}
/*
* CAppRosterMgr * CConf::GetAppRosterManager ()
*
* Private Function Description
* This call returns a pointer to the application manager that
* matches the passed in key. It returns NULL is the application
* does not exists.
*
* Formal Parameters:
* session_key - This is the session key associated with the
* application roster manager that is being
* requested.
*
* Return Value
* A pointer to the appropriate application roster manager.
* NULL if on does not exists.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
CAppRosterMgr * CConf::
GetAppRosterManager ( PGCCSessionKey session_key )
{
CAppRosterMgr *app_roster_manager = NULL;
if (session_key != NULL)
{
CAppRosterMgr *lpAppRosterMgr;
m_AppRosterMgrList.Reset();
while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate()))
{
if (lpAppRosterMgr->IsThisYourSessionKey(session_key))
{
app_roster_manager = lpAppRosterMgr;
break;
}
}
}
return (app_roster_manager);
}
/*
* CConf::GetNewUserIDTag ()
*
* Private Function Description
* This routine generates a User ID Tag number that is used in a
* User ID indication sent betweek two connected nodes.
*
* Formal Parameters:
* None.
*
* Return Value
* This is the User ID tag number generated by this routine.
*
* Side Effects
* None.
*
* Caveats
* Zero is not a valid. We initialize the convener user ID tag to
* zero which is an invalid tag.
*/
TagNumber CConf::
GetNewUserIDTag ( void )
{
/*
** Determine the tag number to associate with the GCC User ID
** that will be returned after the pending request or confirm.
*/
while (1)
{
if (++m_nUserIDTagNumber != 0)
{
if (m_ConnHdlTagNumberList2.Find(m_nUserIDTagNumber) == 0)
break;
}
}
return (m_nUserIDTagNumber);
}
/*
* CConf::DoesRequesterHavePrivilege ()
*
* Private Function Description
* This routine determines if the specified user has the specified
* privilege.
*
* Formal Parameters:
* requester_id - This is the node ID that is being checked for
* the specified privilege.
* privilege - Privilege being checked for.
*
* Return Value
* TRUE - If requester has privilege.
* FALSE - If requester does NOT have privilege.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
BOOL CConf::
DoesRequesterHavePrivilege
(
UserID requester_id,
ConferencePrivilegeType privilege
)
{
BOOL rc = FALSE;
if (requester_id == m_nConvenerNodeID)
rc = TRUE;
else
{
/*
** First check to see if the node is the conductor and a conductor
** privilege list exists. Next check to see if the conference is in
** conducted mode and a conducted mode privilege list exists.
** Else, if not in conducted mode and a Non-Conducted mode privilege
** list exists use it.
*/
if (m_nConductorNodeID == requester_id)
{
if (m_pConductorPrivilegeList != NULL)
{
rc = m_pConductorPrivilegeList->
IsPrivilegeAvailable(privilege);
}
}
if (rc == FALSE)
{
if (m_nConductorNodeID != 0)
{
if (m_pConductModePrivilegeList != NULL)
{
rc = m_pConductModePrivilegeList->IsPrivilegeAvailable(privilege);
}
}
else
{
if (m_pNonConductModePrivilegeList != NULL)
{
rc = m_pNonConductModePrivilegeList->IsPrivilegeAvailable(privilege);
}
}
}
}
return rc;
}
/*
* CConf::SendFullRosterRefresh ()
*
* Private Function Description
* When a new node is added to the conference it is the Top Provider's
* responsiblity to send out a complete refresh of all the rosters
* including both the conference roster and all the application rosters.
* That is the responsiblity of the routine.
*
* Formal Parameters:
* None.
*
* Return Value
* GCC_NO_ERROR - No error.
* GCC_ALLOCATION_FAILURE - A resource error occured.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
GCCError CConf::
SendFullRosterRefresh ( void )
{
GCCError rc;
GCCPDU gcc_pdu;
PSetOfApplicationInformation * application_information;
PSetOfApplicationInformation next_set_of_information;
DebugEntry(CConf::SendFullRosterRefresh);
/*
** Start building the roster update indication. Not that this update
** will include the conference roster as well as all the application
** rosters.
*/
gcc_pdu.choice = INDICATION_CHOSEN;
gcc_pdu.u.indication.choice = ROSTER_UPDATE_INDICATION_CHOSEN;
gcc_pdu.u.indication.u.roster_update_indication.application_information =
NULL;
gcc_pdu.u.indication.u.roster_update_indication.refresh_is_full = TRUE;
// Call on the base class to fill in the PDU structure
rc = m_pConfRosterMgr->GetFullRosterRefreshPDU (
&gcc_pdu.u.indication.u.roster_update_indication.node_information);
/*
** If the conference roster get was successful we will iterate through
** all the application roster managers making the same request for a
** full refresh. Note that the application_information pointer is updated
** after every request to an app roster manager. This is because new
** sets of application information are being allocated everytime this call
** is made.
*/
if (rc == GCC_NO_ERROR)
{
CAppRosterMgr *lpAppRosterMgr;
application_information = &gcc_pdu.u.indication.u.
roster_update_indication.application_information;
m_AppRosterMgrList.Reset();
while (NULL != (lpAppRosterMgr = m_AppRosterMgrList.Iterate()))
{
next_set_of_information = lpAppRosterMgr->GetFullRosterRefreshPDU (
application_information,
&rc);
if (rc == GCC_NO_ERROR)
{
if (next_set_of_information != NULL)
application_information = &next_set_of_information->next;
//
// LONCHANC: If next_set_of_information is NULL,
// then application_information is unchanged.
// This means we effectively ignore this iteration.
// This is good because we do not lose anything.
//
}
else
break;
}
}
/*
** If no errors have occured up to this point we will go ahead and send
** out the PDU.
*/
if (rc == GCC_NO_ERROR)
m_pMcsUserObject->RosterUpdateIndication (&gcc_pdu, FALSE);
DebugExitINT(CConf::SendFullRosterRefresh, rc);
return rc;
}
/*
* CConf::UpdateNewConferenceNode ()
*
* Private Function Description
*
* Formal Parameters:
* None.
*
* Return Value
* GCC_NO_ERROR - No error.
* GCC_ALLOCATION_FAILURE - A resource error occured.
*
* Side Effects
* None.
*
* Caveats
* None.
*/
GCCError CConf::
UpdateNewConferenceNode ( void )
{
GCCError rc = GCC_NO_ERROR;
DebugEntry(CConf::UpdateNewConferenceNode);
// Here we send a full roster refresh for the node that added
rc = SendFullRosterRefresh ();
if (rc == GCC_NO_ERROR)
{
/*
** We must inform the new node of the current conductorship
** status. Note that we only do this if the conference is
** conductible and we are the Top Provider.
*/
if (m_fConfLocked == CONFERENCE_IS_LOCKED)
{
m_pMcsUserObject->SendConferenceLockIndication(
TRUE, // Indicates uniform send
0);
}
else
{
m_pMcsUserObject->SendConferenceUnlockIndication(
TRUE, // Indicates uniform send
0);
}
if (m_fConfConductible)
{
if (m_nConductorNodeID != 0)
{
m_pMcsUserObject->SendConductorAssignIndication(m_nConductorNodeID);
}
else
m_pMcsUserObject->SendConductorReleaseIndication();
}
}
else
{
ERROR_OUT(("CConf: UpdateNewConferenceNode: Error sending full refresh"));
InitiateTermination(GCC_REASON_ERROR_LOW_RESOURCES, 0);
}
DebugExitINT(CConf::UpdateNewConferenceNode, rc);
return rc;
}
/*
** Before we start the disconnect/termination process we must remove all the
** outstanding invite request from our list and send back associated
** confirms. Here we go ahead disconnect all connection associated with
** the invites.
*/
void CConf::
DeleteOutstandingInviteRequests ( void )
{
INVITE_REQ_INFO *pInvReqInfo;
while (NULL != (pInvReqInfo = m_InviteRequestList.Get()))
{
DeleteInviteRequest(pInvReqInfo);
}
}
void CConf::
CancelInviteRequest ( ConnectionHandle hInviteReqConn )
{
INVITE_REQ_INFO *pInvReqInfo;
m_InviteRequestList.Reset();
while (NULL != (pInvReqInfo = m_InviteRequestList.Iterate()))
{
if (hInviteReqConn == pInvReqInfo->connection_handle)
{
m_InviteRequestList.Remove(pInvReqInfo);
DeleteInviteRequest(pInvReqInfo);
return;
}
}
}
void CConf::
DeleteInviteRequest ( INVITE_REQ_INFO *pInvReqInfo )
{
// Cleanup the connection handle list
ASSERT(NULL != pInvReqInfo);
ASSERT(0 != pInvReqInfo->connection_handle);
m_ConnHandleList.Remove(pInvReqInfo->connection_handle);
g_pMCSIntf->DisconnectProviderRequest(pInvReqInfo->connection_handle);
// Send the invite confirm
g_pControlSap->ConfInviteConfirm(m_nConfID,
NULL,
GCC_RESULT_INVALID_CONFERENCE,
pInvReqInfo->connection_handle);
// Free up the invite request info structure
if (NULL != pInvReqInfo->user_data_list)
{
pInvReqInfo->user_data_list->Release();
}
delete pInvReqInfo;
}
void CConf::
ProcessConfJoinResponse
(
PUserJoinResponseInfo join_response_info
)
{
BOOL_PTR bptr;
if (NULL != (bptr = m_JoinRespNamePresentConnHdlList2.Remove(join_response_info->connection_handle)))
{
ConfJoinIndResponse (
(ConnectionHandle)join_response_info->connection_handle,
join_response_info->password_challenge,
join_response_info->user_data_list,
(bptr != FALSE_PTR),
FALSE,
join_response_info->result);
}
}
void CConf::
ProcessAppInvokeIndication
(
CInvokeSpecifierListContainer *pInvokeList,
UserID uidInvoker
)
{
/*
** Here we pass the invoke along to all the enrolled application
** SAPs as well as the control SAP.
*/
g_pControlSap->AppInvokeIndication(m_nConfID, pInvokeList, uidInvoker);
/*
** We iterate on a temporary list to avoid any problems
** if the application sap leaves during the callback.
*/
CAppSap *pAppSap;
CAppSapList TempList(m_RegisteredAppSapList);
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
if (DoesSAPHaveEnrolledAPE(pAppSap))
{
pAppSap->AppInvokeIndication(m_nConfID, pInvokeList, uidInvoker);
}
}
}
#ifdef JASPER
void CConf::
ProcessConductorPermitAskIndication
(
PPermitAskIndicationInfo indication_info
)
{
// Ignore this indication if the conference is not conductible
if (m_fConfConductible &&
(m_nConductorNodeID == m_pMcsUserObject->GetMyNodeID()))
{
g_pControlSap->ConductorPermitAskIndication(
m_nConfID,
indication_info->permission_is_granted,
indication_info->sender_id);
}
}
#endif // JASPER
void CConf::
ProcessConfAddResponse
(
PAddResponseInfo add_response_info
)
{
CNetAddrListContainer *network_address_list;
if (NULL != (network_address_list = m_AddRequestList.Remove(add_response_info->add_request_tag)))
{
g_pControlSap->ConfAddConfirm(
m_nConfID,
network_address_list,
add_response_info->user_data_list,
add_response_info->result);
// Unlock and remove the net address list
network_address_list->UnLockNetworkAddressList();
}
}
void CConf::
ConfRosterReportIndication ( CConfRosterMsg * pMsg )
{
// First send the update to the Control Sap.
g_pControlSap->ConfRosterReportIndication(m_nConfID, pMsg);
// Next send the update to all the Enrolled Application Saps
#if 0 // LONCHANC: app sap does not support conf roster report indication
/*
** We iterate on a temporary list to avoid any problems
** if the application sap leaves during the callback.
*/
CAppSap *pAppSap;
CAppSapList TempList(m_RegisteredAppSapList);
TempList.Reset();
while (NULL != (pAppSap = TempList.Iterate()))
{
if (DoesSAPHaveEnrolledAPE(pAppSap))
{
pAppSap->ConfRosterReportIndication(m_nConfID, pMsg);
}
}
#endif // 0
}
int KeyCompare(const struct Key *key1, const struct Key *key2)
{
if (key1->choice != key2->choice)
return 1;
switch (key1->choice) {
case object_chosen:
return ASN1objectidentifier_cmp((struct ASN1objectidentifier_s **) &key1->u.object,
(struct ASN1objectidentifier_s **) &key2->u.object);
case h221_non_standard_chosen:
if (key1->u.h221_non_standard.length != key2->u.h221_non_standard.length)
return 1;
return memcmp(&key1->u.h221_non_standard.value,
&key2->u.h221_non_standard.value,
key1->u.h221_non_standard.length);
}
return 1;
}
BOOL CConf::
DoesRosterPDUContainApplet(PGCCPDU roster_update,
const struct Key *app_proto_key, BOOL refreshonly)
{
BOOL rc = FALSE;
PSetOfApplicationInformation set_of_application_info;
ASN1choice_t choice;
PSessionKey session_key;
DebugEntry(CConf::DoesRosterPDUContainApplet);
set_of_application_info = roster_update->u.indication.u.
roster_update_indication.application_information;
while (set_of_application_info != NULL)
{
choice = set_of_application_info->value.application_record_list.choice;
session_key = &set_of_application_info->value.session_key;
if (refreshonly && (choice != application_record_refresh_chosen))
continue;
if (!refreshonly && (choice == application_no_change_chosen))
continue;
if (0 == KeyCompare(&session_key->application_protocol_key,
app_proto_key))
{
rc = TRUE;
break;
}
set_of_application_info = set_of_application_info->next;
}
DebugExitINT(CConf::DoesRosterPDUContainApplet, rc);
return rc;
}
UINT HexaStringToUINT(LPCTSTR pcszString)
{
ASSERT(pcszString);
UINT uRet = 0;
LPTSTR pszStr = (LPTSTR) pcszString;
while (_T('\0') != pszStr[0])
{
if ((pszStr[0] >= _T('0')) && (pszStr[0] <= _T('9')))
{
uRet = (16 * uRet) + (BYTE) (pszStr[0] - _T('0'));
}
else if ((pszStr[0] >= _T('a')) && (pszStr[0] <= _T('f')))
{
uRet = (16 * uRet) + (BYTE) (pszStr[0] - _T('a') + 10);
}
else if ((pszStr[0] >= _T('A')) && (pszStr[0] <= _T('F')))
{
uRet = (16 * uRet) + (BYTE) (pszStr[0] - _T('A') + 10);
}
else
ASSERT(0);
pszStr++; // NOTE: DBCS characters are not allowed!
}
return uRet;
}
void CConf::AddNodeVersion(UserID NodeId, NodeRecord *pNodeRecord)
{
PSetOfUserData set_of_user_data;
ASN1octetstring_t user_data;
ASN1octet_t *currpos;
TCHAR szVersion[256];
if (pNodeRecord->bit_mask&RECORD_USER_DATA_PRESENT)
{
set_of_user_data = pNodeRecord->record_user_data;
while (set_of_user_data)
{
if (set_of_user_data->user_data_element.bit_mask & USER_DATA_FIELD_PRESENT)
{
user_data = set_of_user_data->user_data_element.user_data_field;
// Looking for the octet string L"VER:"
currpos = user_data.value;
while (currpos + sizeof(L"VER:") < user_data.value + user_data.length)
{
if (!memcmp(currpos, L"VER:", 8))
{
break;
}
currpos++;
}
if (currpos + sizeof(L"VER:") < user_data.value + user_data.length)
{ // found
WideCharToMultiByte(CP_ACP, 0, (const unsigned short*)(currpos+8),
4 /* only need version num, "0404" */,
szVersion, 256, 0, 0);
szVersion[4] = '\0';
DWORD dwVer = HexaStringToUINT(szVersion);
m_NodeVersionList2.Append(NodeId, dwVer);
WARNING_OUT(("Insert version %x0x for node %d.\n", dwVer, NodeId));
}
}
set_of_user_data = set_of_user_data->next;
}
}
}
GCCError CConf::UpdateNodeVersionList(PGCCPDU roster_update,
GCCNodeID sender_id)
{
GCCError rc = GCC_NO_ERROR;
NodeRecordList node_record_list;
ASN1choice_t choice;
PSetOfNodeRecordRefreshes set_of_node_refresh;
PSetOfNodeRecordUpdates set_of_node_update;
UserID node_id;
NodeRecord *pNodeRecord;
node_record_list = roster_update->u.indication.u.roster_update_indication.
node_information.node_record_list;
switch(node_record_list.choice)
{
case node_no_change_chosen:
break;
case node_record_refresh_chosen:
set_of_node_refresh = node_record_list.u.node_record_refresh;
while (set_of_node_refresh)
{
node_id = set_of_node_refresh->value.node_id;
pNodeRecord = &set_of_node_refresh->value.node_record;
AddNodeVersion(node_id, pNodeRecord);
set_of_node_refresh = set_of_node_refresh->next;
}
break;
case node_record_update_chosen:
set_of_node_update = node_record_list.u.node_record_update;
while (set_of_node_update)
{
node_id = set_of_node_update->value.node_id;
switch(set_of_node_update->value.node_update.choice)
{
case node_remove_record_chosen:
m_NodeVersionList2.Remove(node_id);
break;
case node_add_record_chosen:
pNodeRecord = &set_of_node_update->value.node_update.u.node_add_record;
AddNodeVersion(node_id, pNodeRecord);
break;
}
set_of_node_update = set_of_node_update->next;
}
break;
}
return rc;
}
BOOL CConf::HasNM2xNode(void)
{
DWORD_PTR dwVer;
m_NodeVersionList2.Reset();
while (NULL != (dwVer = m_NodeVersionList2.Iterate()))
{
if (dwVer < 0x0404)
return TRUE;
}
return FALSE;
}
DWORD_PTR WINAPI T120_GetNodeVersion(GCCConfID ConfId, GCCNodeID NodeId)
{
CConf *pConf = g_pGCCController->GetConfObject(ConfId);
DWORD_PTR version;
if (pConf)
{
version = pConf->GetNodeVersion(NodeId);
return version;
}
return 0;
}