/****************************************************************************
 *
 *	$Archive:   S:/STURGEON/SRC/CALLCONT/VCS/q931man.c_v  $
 *
 *  INTEL Corporation Prorietary Information
 *
 *  This listing is supplied under the terms of a license agreement
 *  with INTEL Corporation and may not be copied nor disclosed except
 *  in accordance with the terms of that agreement.
 *
 *	Copyright (c) 1993-1994 Intel Corporation.
 *
 *	$Revision:   1.138  $
 *	$Date:   04 Mar 1997 09:43:22  $
 *	$Author:   MANDREWS  $
 *
 *	Deliverable:
 *
 *	Abstract:
 *		
 *
 *	Notes:
 *
 ***************************************************************************/

#include "precomp.h"

#include "apierror.h"
#include "incommon.h"
#include "callcont.h"
#include "q931.h"
#include "ccmain.h"
#include "listman.h"
#include "q931man.h"
#include "userman.h"
#include "callman.h"
#include "confman.h"
#include "h245man.h"
#include "linkapi.h"
#include "ccutils.h"


extern CALL_CONTROL_STATE	CallControlState;
extern THREADCOUNT			ThreadCount;
extern CC_CONFERENCEID		InvalidConferenceID;


HRESULT InitQ931Manager()
{
	return CC_OK;
}



HRESULT DeInitQ931Manager()
{
	return CC_OK;
}



DWORD _GenerateListenCallback(		PLISTEN					pListen,
									HQ931CALL				hQ931Call,
									PCSS_CALL_INCOMING		pCallIncomingData)
{
HRESULT						status;
CC_HLISTEN					hListen;
CC_LISTEN_CALLBACK_PARAMS	ListenCallbackParams;
PCALL						pCall;
CC_HCALL					hCall;

	ASSERT(pListen != NULL);
	ASSERT(pCallIncomingData != NULL);

	hListen = pListen->hListen;

	status = AllocAndLockCall(
		&hCall,							// pointer to call handle
		CC_INVALID_HANDLE,				// conference handle
		hQ931Call,						// Q931 call handle
		CC_INVALID_HANDLE,				// Q931 call handle for third party invitor
		pCallIncomingData->pCalleeAliasList,
		pCallIncomingData->pCallerAliasList,
		NULL,							// pPeerExtraAliasNames
		NULL,							// pPeerExtension
		NULL,							// local non-standard data
		pCallIncomingData->pNonStandardData,	// remote non-standard data
		NULL,							// local display value
		pCallIncomingData->pszDisplay,	// remote display value
		pCallIncomingData->pSourceEndpointType->pVendorInfo,// remote vendor info
		pCallIncomingData->pLocalAddr,	// local address
		pCallIncomingData->pCallerAddr,	// connect address
		NULL,							// destination address
		pCallIncomingData->pSourceAddr, // source call signal address
		CALLEE,							// call direction
		pCallIncomingData->bCallerIsMC,
		0,								// user token; user will specify in AcceptRejectCall
		INCOMING,						// initial call state
		&pCallIncomingData->CallIdentifier, // H225 CallIdentifier
		&pCallIncomingData->ConferenceID,	// conference ID
		&pCall);						// pointer to call object

	if (status != CC_OK) {
		UnlockListen(pListen);
		Q931RejectCall(hQ931Call,				// Q931 call handle
					   CC_REJECT_UNDEFINED_REASON,	// reject reason
					   &pCallIncomingData->ConferenceID,
					   NULL,					// alternate address
					   NULL);					// non-standard data
		return 1;
	}	

	// Map from Q.931 goals to Call Control goals
	switch (pCallIncomingData->wGoal) {
		case CSG_JOIN:
			ListenCallbackParams.wGoal = CC_GOAL_JOIN;
			break;
		case CSG_CREATE:
			ListenCallbackParams.wGoal = CC_GOAL_CREATE;
			break;
		case CSG_INVITE:
			ListenCallbackParams.wGoal = CC_GOAL_INVITE;
			break;
	}

	ListenCallbackParams.hCall = hCall;
	ListenCallbackParams.pCallerAliasNames = pCallIncomingData->pCallerAliasList;
	ListenCallbackParams.pCalleeAliasNames = pCallIncomingData->pCalleeAliasList;
	ListenCallbackParams.pNonStandardData = pCallIncomingData->pNonStandardData;
	ListenCallbackParams.pszDisplay = pCallIncomingData->pszDisplay;
	ListenCallbackParams.pVendorInfo = pCallIncomingData->pSourceEndpointType->pVendorInfo;
	ListenCallbackParams.ConferenceID = pCallIncomingData->ConferenceID;
	ListenCallbackParams.pCallerAddr = pCallIncomingData->pCallerAddr;
	ListenCallbackParams.pCalleeAddr = pCallIncomingData->pLocalAddr;
	ListenCallbackParams.dwListenToken = pListen->dwListenToken;

	UnlockCall(pCall);

	// Invoke the user callback -- the listen object is locked during the callback,
	// but the associated call object is unlocked (to prevent deadlock if
	// CC_AcceptCall() or CC_RejectCall() is called during the callback from a
	// different thread, and the callback thread blocks pending completion of
	// CC_AcceptCall() or CC_RejectCall())
	InvokeUserListenCallback(pListen,
							 CC_OK,
							 &ListenCallbackParams);

	// Need to validate the listen handle; the associated object may have been
	// deleted during the user callback by this thread
	if (ValidateListen(hListen) == CC_OK)
		UnlockListen(pListen);

	status = LockCall(hCall, &pCall);
	if ((status == CC_OK) && (pCall->CallState == INCOMING)) {
		UnlockCall(pCall);
		return 0;	// cause a ringing condition to occur
	} else {
		// call object has been deleted, or exists in a non-incoming state
		if (status == CC_OK)
			// call object exists in a non-incoming state; AcceptRejectCall
			// may have been invoked from the user callback
			UnlockCall(pCall);
//                return 1;       // don't cause a ringing condition to occur
	}
			
//        // We should never reach this point
//        ASSERT(0);
	return 1;
}



DWORD _Q931CallIncoming(			HQ931CALL				hQ931Call,
									CC_HLISTEN				hListen,
									PCSS_CALL_INCOMING		pCallIncomingData)
{
HRESULT						status;
PLISTEN						pListen;
PCONFERENCE					pConference;
PCALL						pCall;
CC_HCALL					hCall;

	ASSERT(hListen != CC_INVALID_HANDLE);
	ASSERT(pCallIncomingData != NULL);
	ASSERT(!EqualConferenceIDs(&pCallIncomingData->ConferenceID, &InvalidConferenceID));

	if ((pCallIncomingData->wGoal != CSG_CREATE) &&
		(pCallIncomingData->wGoal != CSG_JOIN) &&
		(pCallIncomingData->wGoal != CSG_INVITE)) {
		Q931RejectCall(hQ931Call,				// Q931 call handle
					   CC_REJECT_UNDEFINED_REASON,	// reject reason
					   &pCallIncomingData->ConferenceID,
					   NULL,					// alternate address
					   NULL);					// non-standard data
		return 1;
	}

	status = LockListen(hListen, &pListen);
	if (status != CC_OK) {
		// the listen was presumably cancelled by the user,
		// but we haven't informed Call Setup yet
		Q931RejectCall(hQ931Call,				// Q931 call handle
			           CC_REJECT_UNDEFINED_REASON,	// reject reason
					   &pCallIncomingData->ConferenceID,
					   NULL,					// alternate address
					   NULL);					// non-standard data
		return 1;
	}

	// Look for wConferenceID in conference list
	status = LockConferenceID(&pCallIncomingData->ConferenceID, &pConference);
	if (status == CC_OK) {
		// We found a matching conference ID
		if ((pConference->bDeferredDelete) &&
			((pConference->bAutoAccept == FALSE) ||
			 ((pConference->tsMultipointController == TS_TRUE) &&
			 (pCallIncomingData->bCallerIsMC == TRUE)))) {
			UnlockListen(pListen);
			UnlockConference(pConference);
			Q931RejectCall(hQ931Call,				// Q931 call handle
						   CC_REJECT_UNDEFINED_REASON,	// reject reason
						   &pCallIncomingData->ConferenceID,
						   NULL,					// alternate address
						   NULL);					// non-standard data
			return 1;
		} else {
			if (pConference->tsMultipointController == TS_TRUE) {
				if ((pCallIncomingData->pCalleeDestAddr == NULL) ||
					((pCallIncomingData->pCalleeDestAddr != NULL) &&
					 (EqualAddrs(pCallIncomingData->pLocalAddr,
					             pCallIncomingData->pCalleeDestAddr)))) {
					switch (pCallIncomingData->wGoal) {
						case CSG_CREATE:
							UnlockListen(pListen);
							UnlockConference(pConference);
							Q931RejectCall(hQ931Call,				// Q931 call handle
										   CC_REJECT_UNDEFINED_REASON,	// reject reason
										   &pCallIncomingData->ConferenceID,
										   NULL,					// alternate address
										   NULL);					// non-standard data
							return 1;
						case CSG_JOIN:
							if ((pConference->bDeferredDelete) &&
								(pConference->bAutoAccept == TRUE)) {
								// Auto accept
								status = AllocAndLockCall(
									&hCall,							// pointer to call handle
									pConference->hConference,		// conference handle
									hQ931Call,						// Q931 call handle
									CC_INVALID_HANDLE,				// Q931 call handle for third party invitor
									pCallIncomingData->pCalleeAliasList,
									pCallIncomingData->pCallerAliasList,
									NULL,							// pPeerExtraAliasNames
									NULL,							// pPeerExtension
									NULL,							// local non-standard data
									pCallIncomingData->pNonStandardData,	// remote non-standard data
									NULL,							// local display value
									pCallIncomingData->pszDisplay,	// remote display value
									pCallIncomingData->pSourceEndpointType->pVendorInfo,// remote vendor info
									pCallIncomingData->pLocalAddr,	// local address
									pCallIncomingData->pCallerAddr,	// connect address
									NULL,							// destination address
									pCallIncomingData->pSourceAddr, // source call signal address
									CALLEE,							// call type
									pCallIncomingData->bCallerIsMC,
									0,								// user token; user will specify in AcceptRejectCall
									INCOMING,						// initial call state
									&pCallIncomingData->CallIdentifier,  // h225 CallIdentifier
									&pCallIncomingData->ConferenceID,	// conference ID
									&pCall);						// pointer to call object

								if (status != CC_OK) {
									UnlockListen(pListen);
									UnlockConference(pConference);
									Q931RejectCall(hQ931Call,				// Q931 call handle
												   CC_REJECT_UNDEFINED_REASON,	// reject reason
												   &pCallIncomingData->ConferenceID,
												   NULL,					// alternate address
												   NULL);					// non-standard data
									return 1;
								}
								
								AcceptCall(pCall, pConference);
								return 1;	// Don't send back a RINGING indication
							} else {
								UnlockConference(pConference);
								return _GenerateListenCallback(pListen,
															   hQ931Call,
															   pCallIncomingData);
							}
						case CSG_INVITE:
							UnlockListen(pListen);
							UnlockConference(pConference);
							Q931RejectCall(hQ931Call,				// Q931 call handle
										   CC_REJECT_IN_CONF,		// reject reason
										   &pCallIncomingData->ConferenceID,
										   NULL,					// alternate address
										   NULL);					// non-standard data
							return 1;
					} // switch (wGoal)
				} else { // connect addr != destination addr
					switch (pCallIncomingData->wGoal) {
						case CSG_CREATE:
						case CSG_JOIN:
							UnlockListen(pListen);
							UnlockConference(pConference);
							Q931RejectCall(hQ931Call,				// Q931 call handle
										   CC_REJECT_UNDEFINED_REASON,	// reject reason
										   &pCallIncomingData->ConferenceID,
										   NULL,					// alternate address
										   NULL);					// non-standard data
							return 1;
						case CSG_INVITE:
							// 3rd party invite
							if (pCallIncomingData->bCallerIsMC == TRUE) {
								UnlockListen(pListen);
								UnlockConference(pConference);
								Q931RejectCall(hQ931Call,				// Q931 call handle
											   CC_REJECT_UNDEFINED_REASON,	// reject reason
											   &pCallIncomingData->ConferenceID,
											   NULL,					// alternate address
											   NULL);					// non-standard data
								return 1;
							}
							status = AllocAndLockCall(
								&hCall,							// pointer to call handle
								pConference->hConference,		// conference handle
								CC_INVALID_HANDLE,				// Q931 call handle
								hQ931Call,						// Q931 call handle for third party invitor
								pCallIncomingData->pCallerAliasList, // local alias names
								pCallIncomingData->pCalleeAliasList, // remote alias names
								NULL,							// pPeerExtraAliasNames
								NULL,							// pPeerExtension
								pCallIncomingData->pNonStandardData, // local non-standard data
								NULL,							// remote non-standard data
								pCallIncomingData->pszDisplay,	// local display value
								NULL,							// remote display value
								NULL,							// remote vendor info
								NULL,							// local address
								pCallIncomingData->pCalleeDestAddr,	// connect address
								pCallIncomingData->pCalleeDestAddr,	// destination address
								pCallIncomingData->pSourceAddr, // source call signal address
								THIRD_PARTY_INTERMEDIARY,			// call type
								TRUE,							// caller (this endpoint) is MC
								0,								// user token; user will specify in AcceptRejectCall
								PLACED,							// initial call state
								&pCallIncomingData->CallIdentifier,  // h225 CallIdentifier
								&pCallIncomingData->ConferenceID,	// conference ID
								&pCall);						// pointer to call object

							if (status != CC_OK) {
								UnlockListen(pListen);
								UnlockConference(pConference);
								Q931RejectCall(hQ931Call,				// Q931 call handle
											   CC_REJECT_UNDEFINED_REASON,	// reject reason
											   &pCallIncomingData->ConferenceID,
											   NULL,					// alternate address
											   NULL);					// non-standard data
								return 1;
							}
							PlaceCall(pCall, pConference);
							UnlockCall(pCall);
							UnlockConference(pConference);
							return 1;	// Don't send back a RINGING indication
					} // switch (wGoal)
				}
			} else { // pConference->tsMultipointController != TS_TRUE
				if ((pCallIncomingData->pCalleeDestAddr == NULL) ||
					((pCallIncomingData->pCalleeDestAddr != NULL) &&
					 (EqualAddrs(pCallIncomingData->pLocalAddr,
					             pCallIncomingData->pCalleeDestAddr)))) {
					switch (pCallIncomingData->wGoal) {
						case CSG_CREATE:
							UnlockListen(pListen);
							UnlockConference(pConference);
							Q931RejectCall(hQ931Call,				// Q931 call handle
										   CC_REJECT_UNDEFINED_REASON,	// reject reason
										   &pCallIncomingData->ConferenceID,
										   NULL,					// alternate address
										   NULL);					// non-standard data
							return 1;
						case CSG_JOIN:
						case CSG_INVITE:
							UnlockConference(pConference);
							return _GenerateListenCallback(pListen,
														   hQ931Call,
														   pCallIncomingData);
					} // switch (wGoal)
				} else { // connect addr != destination addr
					UnlockListen(pListen);
					UnlockConference(pConference);
					Q931RejectCall(hQ931Call,				// Q931 call handle
								   CC_REJECT_UNDEFINED_REASON,	// reject reason
								   &pCallIncomingData->ConferenceID,
								   NULL,					// alternate address
								   NULL);					// non-standard data
					return 1;
				} // connect addr != destination addr
			} // pConference->tsMultipointController != TS_TRUE
		} // Matching conference ID
	} else if (status == CC_BAD_PARAM) {
		// This is OK; it simply means that we did not find a matching conference ID
		if (((pCallIncomingData->pCalleeDestAddr != NULL) &&
			(EqualAddrs(pCallIncomingData->pLocalAddr,
					    pCallIncomingData->pCalleeDestAddr))) ||
		    (pCallIncomingData->pCalleeDestAddr == NULL)) {
				return _GenerateListenCallback(pListen,
											   hQ931Call,
											   pCallIncomingData);
		} else { // connect addr != destination addr
			UnlockListen(pListen);
			Q931RejectCall(hQ931Call,				// Q931 call handle
						   CC_REJECT_UNDEFINED_REASON,	// reject reason
						   &pCallIncomingData->ConferenceID,
						   NULL,					// alternate address
						   NULL);					// non-standard data
			return 1;
		}
	} else { // fatal error in LockConference
		UnlockListen(pListen);
		Q931RejectCall(hQ931Call,				// Q931 call handle
					   CC_REJECT_UNDEFINED_REASON,	// reject reason
					   &pCallIncomingData->ConferenceID,
					   NULL,					// alternate address
					   NULL);					// non-standard data
		return 1;	
	}
	
	// We should never reach this point
	ASSERT(0);
	return 1;
}



DWORD _Q931CallRemoteHangup(		HQ931CALL				hQ931Call,
									CC_HLISTEN				hListen,
									CC_HCALL				hCall)
{
CC_LISTEN_CALLBACK_PARAMS	ListenCallbackParams;
PCALL						pCall;
PLISTEN						pListen;

	if (hCall == CC_INVALID_HANDLE) {
		// Either we've already informed the user of the hangup,
		// or the user has not yet accepted or rejected the incoming
		// call request
		ASSERT(hListen != CC_INVALID_HANDLE);

		if (LockQ931Call(hCall, hQ931Call, &pCall) != CC_OK)
			return 0;

		hCall = pCall->hCall;

		if (pCall->hConference != CC_INVALID_HANDLE) {
			UnlockCall(pCall);
			// XXX -- need bHangupReason
			ProcessRemoteHangup(hCall, hQ931Call, CC_REJECT_NORMAL_CALL_CLEARING);
			return 0;
		}

		if (LockListen(hListen, &pListen) != CC_OK) {
			FreeCall(pCall);
			return 0;
		}

		MarkCallForDeletion(pCall);

		ListenCallbackParams.hCall = pCall->hCall;
		ListenCallbackParams.pCallerAliasNames = pCall->pPeerAliasNames;
		ListenCallbackParams.pCalleeAliasNames = pCall->pLocalAliasNames;
		ListenCallbackParams.pNonStandardData = pCall->pPeerNonStandardData;
		ListenCallbackParams.pszDisplay = pCall->pszPeerDisplay;
		ListenCallbackParams.pVendorInfo = pCall->pPeerVendorInfo;
		ListenCallbackParams.wGoal = CC_GOAL_CREATE;	// igonred in this callback
		ListenCallbackParams.ConferenceID = pCall->ConferenceID;
		ListenCallbackParams.pCallerAddr = pCall->pQ931PeerConnectAddr;
		ListenCallbackParams.pCalleeAddr = pCall->pQ931LocalConnectAddr;
		ListenCallbackParams.dwListenToken = pListen->dwListenToken;

		InvokeUserListenCallback(pListen,
		                         CC_PEER_CANCEL,
								 &ListenCallbackParams);

		// Need to validate the listen and call handles; the associated objects may
		// have been deleted during the user callback by this thread
		if (ValidateListen(hListen) == CC_OK)
			UnlockListen(pListen);
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
	} else
		// XXX -- need bHangupReason
		ProcessRemoteHangup(hCall, hQ931Call, CC_REJECT_NORMAL_CALL_CLEARING);

	return 0;
}



DWORD _Q931CallRejected(			HQ931CALL				hQ931Call,
									CC_HCALL				hCall,
									PCSS_CALL_REJECTED		pCallRejectedData)
{
PCALL						pCall;
CC_HCONFERENCE				hConference;
PCONFERENCE					pConference;
HRESULT						status;
CC_CONNECT_CALLBACK_PARAMS	ConnectCallbackParams;
HQ931CALL					hQ931CallInvitor;
CC_ENDPOINTTYPE				SourceEndpointType;
CALLTYPE					CallType;
CC_CONFERENCEID				ConferenceID;
CC_ADDR						SourceAddr;
WORD						wNumCalls;
WORD                        wQ931Goal;
WORD                        wQ931CallType;

	status = LockCall(hCall, &pCall);
	if (status != CC_OK)
		return 0;

	CallType = pCall->CallType;
	ConferenceID = pCall->ConferenceID;

	if ((pCall->hQ931Call != hQ931Call) ||
		((pCall->CallState != PLACED) && (pCall->CallState != RINGING))) {
		// The peer must be in a bad state; we don't expect to receive this message now
		UnlockCall(pCall);
		return 0;
	}

	if (CallType == THIRD_PARTY_INTERMEDIARY) {
		hQ931CallInvitor = pCall->hQ931CallInvitor;
		FreeCall(pCall);
		if (hQ931CallInvitor != CC_INVALID_HANDLE)
			Q931RejectCall(hQ931CallInvitor,
						   pCallRejectedData->bRejectReason,
						   &ConferenceID,
						   NULL,	// alternate address
						   NULL);	// non-standard data
		return 0;
	}

	if (pCall->hConference == CC_INVALID_HANDLE) {
		// Call is not attached to a conference
		FreeCall(pCall);
		return 0;
	}

	UnlockCall(pCall);

	status = LockCallAndConference(hCall, &pCall, &pConference);
	if (status != CC_OK)
		return 0;

	ConnectCallbackParams.pNonStandardData = pCallRejectedData->pNonStandardData;
	ConnectCallbackParams.pszPeerDisplay = NULL;
	ConnectCallbackParams.bRejectReason = pCallRejectedData->bRejectReason;
	ConnectCallbackParams.pTermCapList = NULL;
	ConnectCallbackParams.pH2250MuxCapability = NULL;
	ConnectCallbackParams.pTermCapDescriptors = NULL;	
	ConnectCallbackParams.pLocalAddr = pCall->pQ931LocalConnectAddr;
	if (pCall->pQ931DestinationAddr == NULL)
		ConnectCallbackParams.pPeerAddr = pCall->pQ931PeerConnectAddr;
	else
		ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
	ConnectCallbackParams.pVendorInfo = pCall->pPeerVendorInfo;

	if (pCallRejectedData->bRejectReason == CC_REJECT_ROUTE_TO_MC)
    {
		ConnectCallbackParams.bMultipointConference = TRUE;
        wQ931Goal = CSG_JOIN;
        wQ931CallType = CC_CALLTYPE_N_N;
    }
	else
    {
        // Goal and CallType need to be changed for multipoint support.
		ConnectCallbackParams.bMultipointConference = FALSE;
        wQ931Goal = CSG_CREATE;
        wQ931CallType = CC_CALLTYPE_PT_PT;
    }
	ConnectCallbackParams.pConferenceID = &pCallRejectedData->ConferenceID;
	if (pCallRejectedData->bRejectReason == CC_REJECT_ROUTE_TO_MC)
		ConnectCallbackParams.pMCAddress = pCallRejectedData->pAlternateAddr;
	else
		ConnectCallbackParams.pMCAddress = NULL;
	ConnectCallbackParams.pAlternateAddress = pCallRejectedData->pAlternateAddr;
	ConnectCallbackParams.dwUserToken = pCall->dwUserToken;

	// save a copy of the conference handle; we'll need it to validate
	// the conference object after returning from the user callback
	hConference = pConference->hConference;

	if (((pCallRejectedData->bRejectReason == CC_REJECT_ROUTE_TO_MC) ||
		 (pCallRejectedData->bRejectReason == CC_REJECT_CALL_FORWARDED) ||
		 (pCallRejectedData->bRejectReason == CC_REJECT_ROUTE_TO_GATEKEEPER)) &&
		(EqualConferenceIDs(&pCallRejectedData->ConferenceID, &pCall->ConferenceID)) &&
		(pCallRejectedData->pAlternateAddr != NULL)) {
		// XXX - In order to be H.323 compliant here, we need to re-permission this call
		// through the gatekeeper because:
		// 1. The rerouted call may be going to another gatekeeper zone.
		// 2. The alternate address may be NULL, and we may have to resolve the
		//    alternate alias name list through the gatekeeper
		SourceEndpointType.pVendorInfo = pConference->pVendorInfo;
		SourceEndpointType.bIsTerminal = TRUE;
		SourceEndpointType.bIsGateway = FALSE;

		// Cause our local Q.931 connect address to be placed in the
		// Q.931 setup-UUIE sourceAddress field
		SourceAddr.nAddrType = CC_IP_BINARY;
		SourceAddr.bMulticast = FALSE;
		SourceAddr.Addr.IP_Binary.dwAddr = 0;
		SourceAddr.Addr.IP_Binary.wPort = 0;

		status = Q931PlaceCall(&pCall->hQ931Call,			// Q931 call handle
			                   pCall->pszLocalDisplay,
			                   pCall->pLocalAliasNames,
							   pCall->pPeerAliasNames,
                               pCall->pPeerExtraAliasNames,	// pExtraAliasList
                               pCall->pPeerExtension,		// pExtensionAliasItem
			                   pCall->pLocalNonStandardData,// non-standard data
							   &SourceEndpointType,
                               NULL,						// pszCalledPartyNumber
							   pCallRejectedData->pAlternateAddr, // connect address
							   pCall->pQ931DestinationAddr,	// destination address
							   NULL,						// source address
							   FALSE,						// bIsMC
							   &pCall->ConferenceID,		// conference ID
							   wQ931Goal,					// goal
							   wQ931CallType,				// call type
							   hCall,						// user token
							   (Q931_CALLBACK)Q931Callback,	// callback
#ifdef GATEKEEPER
                               pCall->GkiCall.usCRV,        // CRV
                               &pCall->CallIdentifier);     // H.225 CallIdentifier
#else
                               0,                           // CRV
                               &pCall->CallIdentifier);     // H.225 CallIdentifier
#endif GATEKEEPER
		if (status != CS_OK) {
			MarkCallForDeletion(pCall);
			InvokeUserConferenceCallback(pConference,
										 CC_CONNECT_INDICATION,
										 status,
										 &ConnectCallbackParams);

			if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
				FreeCall(pCall);

			if (ValidateConference(hConference) != CC_OK)
				return 0;

			for ( ; ; ) {
				// Start up an enqueued call, if one exists
				status = RemoveEnqueuedCallFromConference(pConference, &hCall);
				if ((status != CC_OK) || (hCall == CC_INVALID_HANDLE))
					break;

				status = LockCall(hCall, &pCall);
				if (status == CC_OK) {
					pCall->CallState = PLACED;

					status = PlaceCall(pCall, pConference);
					UnlockCall(pCall);
					if (status == CC_OK)
						break;
				}
			}
			return 0;
		}
		UnlockCall(pCall);
		UnlockConference(pConference);
		return 0;
	}

	MarkCallForDeletion(pCall);

	if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
		((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED))) {
		InvokeUserConferenceCallback(pConference,
									 CC_CONNECT_INDICATION,
									 CC_PEER_REJECT,
									 &ConnectCallbackParams);
	}
	
	if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
		FreeCall(pCall);

	// Need to validate conference handle; the associated object may
	// have been deleted during the user callback in this thread
	if (ValidateConference(hConference) != CC_OK)
		return 0;

	for ( ; ; ) {
		// Start up an enqueued call, if one exists
		status = RemoveEnqueuedCallFromConference(pConference, &hCall);
		if ((status != CC_OK) || (hCall == CC_INVALID_HANDLE))
			break;

		status = LockCall(hCall, &pCall);
		if (status == CC_OK) {
			pCall->CallState = PLACED;

			status = PlaceCall(pCall, pConference);
			UnlockCall(pCall);
			if (status == CC_OK)
				break;
		}
	}
	
	EnumerateCallsInConference(&wNumCalls, NULL, pConference, REAL_CALLS);

	if (wNumCalls == 0) {
		if (pConference->bDeferredDelete) {
			ASSERT(pConference->LocalEndpointAttached == DETACHED);
			FreeConference(pConference);
		} else {
			if ((pConference->ConferenceMode != MULTIPOINT_MODE) ||
				(pConference->tsMultipointController != TS_TRUE))
				ReInitializeConference(pConference);
			UnlockConference(pConference);
		}
	} else {
		UnlockConference(pConference);
	}
	return 0;
}



DWORD _Q931CallAccepted(			HQ931CALL				hQ931Call,
									CC_HCALL				hCall,
									PCSS_CALL_ACCEPTED		pCallAcceptedData)
{
HRESULT						status;
PCALL						pCall;
CC_HCONFERENCE				hConference;
PCONFERENCE					pConference;
CC_CONNECT_CALLBACK_PARAMS	ConnectCallbackParams;
BYTE						bTerminalType;
BOOL						bMultipointConference;
CALLTYPE					CallType;
HQ931CALL					hQ931CallInvitor;
H245_INST_T					H245Instance;
DWORD                       dwLinkLayerPhysicalId;

	status = LockCallAndConference(hCall, &pCall, &pConference);
	if (status != CC_OK)
		return 0;

	CallType = pCall->CallType;
	hConference = pConference->hConference;
	hQ931Call = pCall->hQ931Call;
	hQ931CallInvitor = pCall->hQ931CallInvitor;

	ASSERT((pCall->hQ931Call == hQ931Call) || (pCall->hQ931CallInvitor == hQ931Call));

	if ((pConference->ConferenceMode == POINT_TO_POINT_MODE) ||
		(pConference->ConferenceMode == MULTIPOINT_MODE))
		bMultipointConference = TRUE;
	else
		bMultipointConference = FALSE;

	// Initialize ConnectCallbackParams
	ConnectCallbackParams.pNonStandardData = pCallAcceptedData->pNonStandardData;
	ConnectCallbackParams.pszPeerDisplay = pCallAcceptedData->pszDisplay;
	ConnectCallbackParams.bRejectReason = CC_REJECT_UNDEFINED_REASON;	// field ignored
	ConnectCallbackParams.pTermCapList = NULL;
	ConnectCallbackParams.pH2250MuxCapability = NULL;
	ConnectCallbackParams.pTermCapDescriptors = NULL;
	ConnectCallbackParams.pLocalAddr = pCall->pQ931LocalConnectAddr;
	ConnectCallbackParams.pPeerAddr = pCallAcceptedData->pCalleeAddr;
	ConnectCallbackParams.pVendorInfo = pCall->pPeerVendorInfo;
	ConnectCallbackParams.bMultipointConference = bMultipointConference;
	ConnectCallbackParams.pConferenceID = &pConference->ConferenceID;
	ConnectCallbackParams.pMCAddress = pConference->pMultipointControllerAddr;
	ConnectCallbackParams.pAlternateAddress = NULL;
	ConnectCallbackParams.dwUserToken = pCall->dwUserToken;

	if (pCallAcceptedData->pCalleeAddr) {
		// Set pCall->pQ931DestinationAddr to the destination address that we got from Q931.
		// Note that we may not current have a destination address (if the client didn't
		// specify one), or we may currently have a destination address	in domain name format
		// which we need to change to binary format
		if (pCall->pQ931DestinationAddr == NULL)
			pCall->pQ931DestinationAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
		if (pCall->pQ931DestinationAddr != NULL)
			*pCall->pQ931DestinationAddr = *pCallAcceptedData->pCalleeAddr;
	}

	if ((!EqualConferenceIDs(&pConference->ConferenceID, &InvalidConferenceID)) &&
		(!EqualConferenceIDs(&pConference->ConferenceID, &pCallAcceptedData->ConferenceID))) {

		MarkCallForDeletion(pCall);

		if (CallType == THIRD_PARTY_INTERMEDIARY) {
			if (hQ931CallInvitor != CC_INVALID_HANDLE)
				Q931RejectCall(hQ931CallInvitor,
							   CC_REJECT_UNDEFINED_REASON,
							   &pCallAcceptedData->ConferenceID,
							   NULL,	// alternate address
							   pCallAcceptedData->pNonStandardData);
		} else {
			if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
			    ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED)))
				InvokeUserConferenceCallback(pConference,
											 CC_CONNECT_INDICATION,
											 CC_INTERNAL_ERROR,
											 &ConnectCallbackParams);
		}
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
		if (ValidateConference(hConference) == CC_OK)
			UnlockConference(pConference);
		Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
		return 0;
	}

	pConference->ConferenceID = pCallAcceptedData->ConferenceID;
	pCall->ConferenceID = pCallAcceptedData->ConferenceID;
	// Copy the newly-supplied peer address into the call object.
	// This is preferable if the original peer address was in IP dot
	// or domain name format
	if (CallType != THIRD_PARTY_INVITOR) {
		if (pCallAcceptedData->pCalleeAddr != NULL) {
			if (pCall->pQ931DestinationAddr == NULL)
				pCall->pQ931DestinationAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
			if (pCall->pQ931DestinationAddr != NULL)
				*pCall->pQ931DestinationAddr = *pCallAcceptedData->pCalleeAddr;
		}
		
		if (pCallAcceptedData->pLocalAddr != NULL) {
			if (pCall->pQ931LocalConnectAddr == NULL)
				pCall->pQ931LocalConnectAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
			if (pCall->pQ931LocalConnectAddr != NULL)
				*pCall->pQ931LocalConnectAddr = *pCallAcceptedData->pLocalAddr;
		}
	}

	ASSERT(pCall->pPeerNonStandardData == NULL);
	CopyNonStandardData(&pCall->pPeerNonStandardData,
		                pCallAcceptedData->pNonStandardData);

	ASSERT(pCall->pszPeerDisplay == NULL);
	CopyDisplay(&pCall->pszPeerDisplay,
		        pCallAcceptedData->pszDisplay);

	ASSERT(pCall->pPeerVendorInfo == NULL);
	CopyVendorInfo(&pCall->pPeerVendorInfo,
	               pCallAcceptedData->pDestinationEndpointType->pVendorInfo);

	if (CallType == THIRD_PARTY_INVITOR) {
		pCall->CallState = CALL_COMPLETE;
		ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
		MarkCallForDeletion(pCall);
		InvokeUserConferenceCallback(pConference,
									 CC_CONNECT_INDICATION,
									 CC_OK,
									 &ConnectCallbackParams);
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
		if (ValidateConference(hConference) == CC_OK)
			UnlockConference(pConference);
		return 0;
	}

	pCall->CallState = TERMCAP;

	status = MakeH245PhysicalID(&pCall->dwH245PhysicalID);
	if (status != CC_OK) {
		
		MarkCallForDeletion(pCall);

		if (CallType == THIRD_PARTY_INTERMEDIARY) {
			if (hQ931CallInvitor != CC_INVALID_HANDLE)
				Q931RejectCall(hQ931CallInvitor,
							   CC_REJECT_UNDEFINED_REASON,
							   &pCallAcceptedData->ConferenceID,
							   NULL,	// alternate address
							   pCallAcceptedData->pNonStandardData);
		} else {
			if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
			    ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED)))
				InvokeUserConferenceCallback(pConference,
											 CC_CONNECT_INDICATION,
											 status,
											 &ConnectCallbackParams);
		}
		Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
		if (ValidateConference(hConference) == CC_OK)
			UnlockConference(pConference);
		return 0;
	}

    //MULTITHREAD
    //Use a tmp ID so we don't clobber the chosen H245Id.
    //   H245Id=>
    //   <= linkLayerId
    dwLinkLayerPhysicalId = INVALID_PHYS_ID;

	SetTerminalType(pConference->tsMultipointController, &bTerminalType);
	pCall->H245Instance = H245Init(H245_CONF_H323,
                                   pCall->dwH245PhysicalID,
                                   &dwLinkLayerPhysicalId,
								   hCall,
								   (H245_CONF_IND_CALLBACK_T)H245Callback,
								   bTerminalType);
	if (pCall->H245Instance == H245_INVALID_ID) {
		MarkCallForDeletion(pCall);
		if (CallType == THIRD_PARTY_INTERMEDIARY) {
			if (hQ931CallInvitor != CC_INVALID_HANDLE)
				Q931RejectCall(hQ931CallInvitor,
							   CC_REJECT_UNDEFINED_REASON,
							   &pCallAcceptedData->ConferenceID,
							   NULL,	// alternate address
							   pCallAcceptedData->pNonStandardData);
		} else {
			if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
			    ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED)))
				InvokeUserConferenceCallback(pConference,
											 CC_CONNECT_INDICATION,
											 CC_INTERNAL_ERROR,
											 &ConnectCallbackParams);
		}
		Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
		if (ValidateConference(hConference) == CC_OK)
			UnlockConference(pConference);
		return 0;
	}

	H245Instance = pCall->H245Instance;

	// XXX -- need to define connect callback routine
    // Send in the Id we got back from H245Init.
    status = linkLayerConnect(dwLinkLayerPhysicalId,
		                      pCallAcceptedData->pH245Addr,
							  NULL);
	if (status != NOERROR) {

		MarkCallForDeletion(pCall);

		if (CallType == THIRD_PARTY_INTERMEDIARY) {
			if (hQ931CallInvitor != CC_INVALID_HANDLE)
				Q931RejectCall(hQ931CallInvitor,
							   CC_REJECT_UNDEFINED_REASON,
							   &pCallAcceptedData->ConferenceID,
							   NULL,	// alternate address
							   pCallAcceptedData->pNonStandardData);
		} else {
			if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
			    ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED)))
				InvokeUserConferenceCallback(pConference,
											 CC_CONNECT_INDICATION,
											 status,
											 &ConnectCallbackParams);
		}
		H245ShutDown(H245Instance);
		Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
		if (ValidateConference(hConference) == CC_OK)
			UnlockConference(pConference);
		return 0;
	}

	pCall->bLinkEstablished = TRUE;

	status = SendTermCaps(pCall, pConference);
	if (status != CC_OK) {

		MarkCallForDeletion(pCall);

		if (CallType == THIRD_PARTY_INTERMEDIARY) {
			if (hQ931CallInvitor != CC_INVALID_HANDLE)
				Q931RejectCall(hQ931CallInvitor,
							   CC_REJECT_UNDEFINED_REASON,
							   &pCallAcceptedData->ConferenceID,
							   NULL,	// alternate address
							   pCallAcceptedData->pNonStandardData);
		} else {
			if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
			    ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED)))
				InvokeUserConferenceCallback(pConference,
											 CC_CONNECT_INDICATION,
											 CC_NO_MEMORY,
											 &ConnectCallbackParams);
		}
		H245ShutDown(H245Instance);
		Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
		if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
			FreeCall(pCall);
		if (ValidateConference(hConference) == CC_OK)
			UnlockConference(pConference);
		return 0;
	}
	
	pCall->OutgoingTermCapState = AWAITING_ACK;

	if (pCall->MasterSlaveState == MASTER_SLAVE_NOT_STARTED) {
		status = H245InitMasterSlave(pCall->H245Instance,
			                         pCall->H245Instance);	// returned as dwTransId in the callback
		if (status != H245_ERROR_OK) {

			MarkCallForDeletion(pCall);

			if (CallType == THIRD_PARTY_INTERMEDIARY) {
				if (hQ931CallInvitor != CC_INVALID_HANDLE)
					Q931RejectCall(hQ931CallInvitor,
								   CC_REJECT_UNDEFINED_REASON,
								   &pCallAcceptedData->ConferenceID,
								   NULL,	// alternate address
								   pCallAcceptedData->pNonStandardData);
			} else {
				if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
			        ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED)))
					InvokeUserConferenceCallback(pConference,
												 CC_CONNECT_INDICATION,
												 status,
												 &ConnectCallbackParams);
			}
			H245ShutDown(H245Instance);
			Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
			if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
				FreeCall(pCall);
			if (ValidateConference(hConference) == CC_OK)
				UnlockConference(pConference);
			return 0;
		}
		pCall->MasterSlaveState = MASTER_SLAVE_IN_PROGRESS;
	}

	UnlockConference(pConference);
	UnlockCall(pCall);
	return 0;
}



DWORD _Q931CallRinging(				HQ931CALL				hQ931Call,
									CC_HCALL				hCall)
{
PCALL						pCall;
CC_HCONFERENCE				hConference;
PCONFERENCE					pConference;
HRESULT						status;
CC_RINGING_CALLBACK_PARAMS	RingingCallbackParams;
CC_CONFERENCEID				ConferenceID;

	status = LockCallAndConference(hCall, &pCall, &pConference);
	if (status != CC_OK) {
		Q931RejectCall(hQ931Call,				// Q931 call handle
		               CC_REJECT_UNDEFINED_REASON,	// reject reason
					   NULL,					// conference ID
					   NULL,					// alternate address
					   NULL);					// non-standard data
		return 0;
	}

	ConferenceID = pCall->ConferenceID;

	if ((pCall->hQ931Call != hQ931Call) || (pCall->CallState != PLACED)) {
		// The peer must be in a bad state; we don't expect to receive this message now
		UnlockCall(pCall);
		return 0;
	}

	pCall->CallState = RINGING;

	if (pCall->CallType == THIRD_PARTY_INTERMEDIARY) {
		// Send "ringing" indication to pCall->hQ931CallInvitor
		Q931Ringing(pCall->hQ931CallInvitor,
			        NULL);	// pCRV
		UnlockConference(pConference);
		UnlockCall(pCall);
		return 0;
	}

	RingingCallbackParams.pNonStandardData = NULL;
	RingingCallbackParams.dwUserToken = pCall->dwUserToken;

	// save a copy of the conference handle; we'll need it to validate
	// the conference object after returning from the user callback
	hConference = pConference->hConference;

	InvokeUserConferenceCallback(pConference,
		                         CC_RINGING_INDICATION,
								 CC_OK,
								 &RingingCallbackParams);
	
	// Need to validate conference and call handles; the associated objects may
	// have been deleted during the user callback in this thread
	if (ValidateConference(hConference) == CC_OK)
		UnlockConference(pConference);
	if (ValidateCall(hCall) == CC_OK)
		UnlockCall(pCall);
	return 0;
}



DWORD _Q931CallFailed(				HQ931CALL				hQ931Call,
									CC_HCALL				hCall,
									PCSS_CALL_FAILED		pCallFailedData)
{
PCALL						pCall;
CC_HCONFERENCE				hConference;
PCONFERENCE					pConference;
HQ931CALL					hQ931CallInvitor;
HRESULT						status;
CC_CONNECT_CALLBACK_PARAMS	ConnectCallbackParams;
CALLTYPE					CallType;
CC_CONFERENCEID				ConferenceID;
WORD						wNumCalls;

	status = LockCallAndConference(hCall, &pCall, &pConference);
	if (status != CC_OK)
		return 0;

	CallType = pCall->CallType;
	ConferenceID = pCall->ConferenceID;

	if (pCall->hQ931Call != hQ931Call) {
		UnlockConference(pConference);
		UnlockCall(pCall);
		return 0;
	}

	if (CallType == THIRD_PARTY_INTERMEDIARY) {
		hQ931CallInvitor = pCall->hQ931CallInvitor;
		FreeCall(pCall);
		UnlockConference(pConference);
		if (hQ931CallInvitor != CC_INVALID_HANDLE)
			Q931RejectCall(hQ931CallInvitor,
						   CC_REJECT_UNREACHABLE_DESTINATION,
						   &ConferenceID,
						   NULL,	// alternate address
						   NULL);	// non-standard data
		return 0;
	}

	ConnectCallbackParams.pNonStandardData = NULL;
	ConnectCallbackParams.pszPeerDisplay = NULL;
	ConnectCallbackParams.bRejectReason = CC_REJECT_UNREACHABLE_DESTINATION;
	ConnectCallbackParams.pTermCapList = NULL;
	ConnectCallbackParams.pH2250MuxCapability = NULL;
	ConnectCallbackParams.pTermCapDescriptors = NULL;	
	ConnectCallbackParams.pLocalAddr = pCall->pQ931LocalConnectAddr;
 	if (pCall->pQ931DestinationAddr == NULL)
		ConnectCallbackParams.pPeerAddr = pCall->pQ931PeerConnectAddr;
	else
		ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
	ConnectCallbackParams.pVendorInfo = pCall->pPeerVendorInfo;
	ConnectCallbackParams.bMultipointConference = FALSE;
	ConnectCallbackParams.pConferenceID = &pConference->ConferenceID;
	ConnectCallbackParams.pMCAddress = NULL;
	ConnectCallbackParams.pAlternateAddress = NULL;
	ConnectCallbackParams.dwUserToken = pCall->dwUserToken;

	// save a copy of the conference handle; we'll need it to validate
	// the conference object after returning from the user callback
	hConference = pConference->hConference;

	MarkCallForDeletion(pCall);

	if ((CallType == CALLER) || (CallType == THIRD_PARTY_INVITOR) ||
	    ((CallType == CALLEE) && (pConference->LocalEndpointAttached == NEVER_ATTACHED))) {
		InvokeUserConferenceCallback(pConference,
									 CC_CONNECT_INDICATION,
									 pCallFailedData->error,
									 &ConnectCallbackParams);
	}
	if (ValidateCallMarkedForDeletion(hCall) == CC_OK)
		FreeCall(pCall);
	// Need to validate conference handle; the associated object may
	// have been deleted during the user callback in this thread
	if (ValidateConference(hConference) != CC_OK)
		return 0;

	for ( ; ; ) {
		// Start up an enqueued call, if one exists
		status = RemoveEnqueuedCallFromConference(pConference, &hCall);
		if ((status != CC_OK) || (hCall == CC_INVALID_HANDLE))
			break;

		status = LockCall(hCall, &pCall);
		if (status == CC_OK) {
			pCall->CallState = PLACED;

			status = PlaceCall(pCall, pConference);
			UnlockCall(pCall);
			if (status == CC_OK)
				break;
		}
	}

	EnumerateCallsInConference(&wNumCalls, NULL, pConference, REAL_CALLS);

	if (wNumCalls == 0) {
		if (pConference->bDeferredDelete) {
			ASSERT(pConference->LocalEndpointAttached == DETACHED);
			FreeConference(pConference);
		} else {
			if ((pConference->ConferenceMode != MULTIPOINT_MODE) ||
				(pConference->tsMultipointController != TS_TRUE))
				ReInitializeConference(pConference);
			UnlockConference(pConference);
		}
	} else {
		UnlockConference(pConference);
	}
	return 0;
}



DWORD _Q931CallConnectionClosed(	HQ931CALL				hQ931Call,
									CC_HCALL				hCall)
{
	return 0;
}



DWORD Q931Callback(					BYTE					bEvent,
									HQ931CALL				hQ931Call,
									DWORD_PTR				dwListenToken,
									DWORD_PTR				dwUserToken,
									void *					pEventData)
{
DWORD	dwStatus;

	EnterCallControl();

	if (CallControlState != OPERATIONAL_STATE)
		DWLeaveCallControl(0);

	switch (bEvent) {
		case Q931_CALL_INCOMING:
			dwStatus = _Q931CallIncoming(hQ931Call, (CC_HLISTEN)dwListenToken,
				                         (PCSS_CALL_INCOMING)pEventData);
			break;

		case Q931_CALL_REMOTE_HANGUP:
			dwStatus = _Q931CallRemoteHangup(hQ931Call, (CC_HLISTEN)dwListenToken,
				                             (CC_HCALL)dwUserToken);
			break;

		case Q931_CALL_REJECTED:
			dwStatus =  _Q931CallRejected(hQ931Call, (CC_HCALL)dwUserToken,
				                          (PCSS_CALL_REJECTED)pEventData);
			break;

		case Q931_CALL_ACCEPTED:
			dwStatus =  _Q931CallAccepted(hQ931Call, (CC_HCALL)dwUserToken,
				                          (PCSS_CALL_ACCEPTED)pEventData);
			break;

		case Q931_CALL_RINGING:
			dwStatus =  _Q931CallRinging(hQ931Call, (CC_HCALL)dwUserToken);
			break;

		case Q931_CALL_FAILED:
			dwStatus = _Q931CallFailed(hQ931Call, (CC_HCALL)dwUserToken,
				                       (PCSS_CALL_FAILED)pEventData);
			break;

		case Q931_CALL_CONNECTION_CLOSED:
			dwStatus = _Q931CallConnectionClosed(hQ931Call, (CC_HCALL)dwUserToken);
			break;

		default:
			ASSERT(0);
			dwStatus = 0;
			break;
	}
	DWLeaveCallControl(dwStatus);
}