/****************************************************************************
 *
 * $Archive:   S:/STURGEON/SRC/CALLCONT/VCS/callcon2.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) 1996 Intel Corporation.
 *
 * $Revision:   1.35  $
 * $Date:   03 Mar 1997 09:08:16  $
 * $Author:   MANDREWS  $
 *
 * Deliverable:
 *
 * Abstract:
 *
 * Notes:
 *
 ***************************************************************************/
#ifdef GATEKEEPER

#pragma warning ( disable : 4057 4115 4201 4214 )
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#pragma warning ( default : 4115 4201 4214 )
#include "apierror.h"
#include "incommon.h"
#include "callcont.h"
#include "q931.h"
#include "ccmain.h"
#include "confman.h"
#include "listman.h"
#include "q931man.h"
#include "h245man.h"
#include "callman.h"
#include "userman.h"
#include "chanman.h"
#include "hangman.h"
#include "linkapi.h"
#include "h245api.h"
#include "ccutils.h"
#include "callman2.h"

#define HResultLeave(x) return x


extern CC_CONFERENCEID	InvalidConferenceID;


//
// Complete CC_xxx Operations
//

HRESULT ListenReject     (DWORD hListen, HRESULT Reason)
{
HRESULT						status;
PLISTEN						pListen;
CC_LISTEN_CALLBACK_PARAMS   ListenCallbackParams;

	status = LockListen(hListen, &pListen);
	if (status == CC_OK) {
		ListenCallbackParams.hCall = CC_INVALID_HANDLE;
		ListenCallbackParams.pCallerAliasNames = NULL;
		ListenCallbackParams.pCalleeAliasNames = NULL;
		ListenCallbackParams.pNonStandardData = NULL;
		ListenCallbackParams.pszDisplay = NULL;
		ListenCallbackParams.pVendorInfo = NULL;
		ListenCallbackParams.ConferenceID = InvalidConferenceID;
		ListenCallbackParams.pCallerAddr = NULL;
		ListenCallbackParams.pCalleeAddr = NULL;
		ListenCallbackParams.dwListenToken = pListen->dwListenToken;

	    // 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,
							     Reason,
							     &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) {
	        HQ931LISTEN hQ931Listen = pListen->hQ931Listen;
		    UnlockListen(pListen);
	        status = Q931CancelListen(hQ931Listen);
	        if (LockListen(hListen, &pListen) == CC_OK) {
                FreeListen(pListen);
            }
        }
    }

    HResultLeave(status);
} // ListenReject()



HRESULT PlaceCallConfirm    (void *pCallVoid, void *pConferenceVoid)
{
    register PCALL          pCall = (PCALL) pCallVoid;
    HRESULT                 status;

    // Free Alias lists
    if (pCall->GkiCall.pCalleeAliasNames != NULL) {
        Q931FreeAliasNames(pCall->GkiCall.pCalleeAliasNames);
        pCall->GkiCall.pCalleeAliasNames = NULL;
    }
    if (pCall->GkiCall.pCalleeExtraAliasNames != NULL) {
        Q931FreeAliasNames(pCall->GkiCall.pCalleeExtraAliasNames);
        pCall->GkiCall.pCalleeExtraAliasNames = NULL;
    }

    if (pCall->pQ931PeerConnectAddr == NULL) {
        pCall->pQ931PeerConnectAddr = (PCC_ADDR)Malloc(sizeof(CC_ADDR));
        if (pCall->pQ931PeerConnectAddr == NULL)
            return PlaceCallReject(pCallVoid, pConferenceVoid, CC_NO_MEMORY);
    }

    pCall->pQ931PeerConnectAddr->nAddrType             = CC_IP_BINARY;
    pCall->pQ931PeerConnectAddr->bMulticast            = FALSE;
    pCall->pQ931PeerConnectAddr->Addr.IP_Binary.wPort  = pCall->GkiCall.wPort;
    pCall->pQ931PeerConnectAddr->Addr.IP_Binary.dwAddr = ntohl(pCall->GkiCall.dwIpAddress);

    status = PlaceCall(pCall, (PCONFERENCE)pConferenceVoid);
    if (status != CC_OK)
      PlaceCallReject(pCallVoid, pConferenceVoid, status);
    return status;
} // PlaceCallConfirm()



HRESULT PlaceCallReject     (void *pCallVoid, void *pConferenceVoid, HRESULT Reason)
{
register PCALL          pCall = (PCALL) pCallVoid;
register PCONFERENCE    pConference = (PCONFERENCE) pConferenceVoid;
CC_HCONFERENCE			hConference;
HRESULT                 status = CC_OK;
CC_CONNECT_CALLBACK_PARAMS ConnectCallbackParams = {0};
CC_HCALL                hCall;
PCALL                   pCall2;

	ASSERT(pCall != NULL);
	ASSERT(pConference != NULL);

    // Free Alias lists
    if (pCall->GkiCall.pCalleeAliasNames != NULL) {
        Q931FreeAliasNames(pCall->GkiCall.pCalleeAliasNames);
        pCall->GkiCall.pCalleeAliasNames = NULL;
    }
    if (pCall->GkiCall.pCalleeExtraAliasNames != NULL) {
        Q931FreeAliasNames(pCall->GkiCall.pCalleeExtraAliasNames);
        pCall->GkiCall.pCalleeExtraAliasNames = NULL;
    }

    // Inform Call Control client of failure
    ConnectCallbackParams.pNonStandardData     = pCall->pPeerNonStandardData;
    ConnectCallbackParams.pszPeerDisplay       = pCall->pszPeerDisplay;
    ConnectCallbackParams.bRejectReason        = 0;
    ConnectCallbackParams.pTermCapList         = pCall->pPeerH245TermCapList;
    ConnectCallbackParams.pH2250MuxCapability  = pCall->pPeerH245H2250MuxCapability;
    ConnectCallbackParams.pTermCapDescriptors  = pCall->pPeerH245TermCapDescriptors;
    ConnectCallbackParams.pLocalAddr           = pCall->pQ931LocalConnectAddr;
 	if (pCall->pQ931DestinationAddr == NULL)
		ConnectCallbackParams.pPeerAddr = pCall->pQ931PeerConnectAddr;
	else
		ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
    ConnectCallbackParams.pVendorInfo          = pCall->pPeerVendorInfo;
    if (pConference->ConferenceMode == MULTIPOINT_MODE)
        ConnectCallbackParams.bMultipointConference = TRUE;
    else
        ConnectCallbackParams.bMultipointConference = FALSE;
    ConnectCallbackParams.pConferenceID        = &pConference->ConferenceID;
    ConnectCallbackParams.pMCAddress           = pConference->pMultipointControllerAddr;
	ConnectCallbackParams.pAlternateAddress	= NULL;
    ConnectCallbackParams.dwUserToken          = pCall->dwUserToken;
	hConference = pConference->hConference;
    InvokeUserConferenceCallback(pConference,
                                 CC_CONNECT_INDICATION,
                                 Reason,
                                 &ConnectCallbackParams);

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

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

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

    HResultLeave(status);
} // PlaceCallReject()



HRESULT AcceptCallConfirm   (void *pCallVoid, void *pConferenceVoid)
{
CC_HCALL        hCall       = ((PCALL)pCallVoid)->hCall;
CC_HCONFERENCE  hConference = ((PCONFERENCE)pConferenceVoid)->hConference;
HRESULT         status;

    status = AcceptCall((PCALL)pCallVoid, (PCONFERENCE)pConferenceVoid);
    LockConference(hConference, (PPCONFERENCE)&pConferenceVoid);
    LockCall(hCall, (PPCALL)&pCallVoid);
    if (status != CC_OK && pCallVoid != NULL && pConferenceVoid != NULL)
      AcceptCallReject(pCallVoid, pConferenceVoid, status);
    return status;
} // AcceptCallConfirm()



HRESULT AcceptCallReject    (void *pCallVoid, void *pConferenceVoid, HRESULT Reason)
{
register PCALL          pCall = (PCALL) pCallVoid;
register PCONFERENCE    pConference = (PCONFERENCE) pConferenceVoid;
HRESULT                 status = CC_OK;
CC_CONNECT_CALLBACK_PARAMS ConnectCallbackParams = {0};

    status = Q931RejectCall(pCall->hQ931Call,       // Q931 call handle
                            CC_REJECT_GATEKEEPER_RESOURCES,
                            &pCall->ConferenceID,   // Conference Identifier
                            NULL,                   // alternate address
                            pCall->pLocalNonStandardData);

    ConnectCallbackParams.pNonStandardData     = pCall->pPeerNonStandardData;
    ConnectCallbackParams.pszPeerDisplay       = pCall->pszPeerDisplay;
    ConnectCallbackParams.bRejectReason        = 0;
    ConnectCallbackParams.pTermCapList         = pCall->pPeerH245TermCapList;
    ConnectCallbackParams.pH2250MuxCapability  = pCall->pPeerH245H2250MuxCapability;
    ConnectCallbackParams.pTermCapDescriptors  = pCall->pPeerH245TermCapDescriptors;
    ConnectCallbackParams.pLocalAddr           = pCall->pQ931LocalConnectAddr;
 	if (pCall->pQ931DestinationAddr == NULL)
		ConnectCallbackParams.pPeerAddr = pCall->pQ931PeerConnectAddr;
	else
		ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
    if (pConference->ConferenceMode == MULTIPOINT_MODE)
        ConnectCallbackParams.bMultipointConference = TRUE;
    else
        ConnectCallbackParams.bMultipointConference = FALSE;
    ConnectCallbackParams.pVendorInfo          = pCall->pPeerVendorInfo;
    ConnectCallbackParams.pConferenceID        = &pConference->ConferenceID;
    ConnectCallbackParams.pMCAddress           = pConference->pMultipointControllerAddr;
	ConnectCallbackParams.pAlternateAddress	= NULL;
    ConnectCallbackParams.dwUserToken          = pCall->dwUserToken;

    InvokeUserConferenceCallback(pConference,
                                 CC_CONNECT_INDICATION,
                                 Reason,
                                 &ConnectCallbackParams);

    HResultLeave(status);
} // AcceptCallReject()



#if 0

HRESULT CancelCallConfirm   (void *pCallVoid, void *pConferenceVoid)
{
PCALL               pCall = (PCALL) pCallVoid;
PCONFERENCE         pConference = (PCONFERENCE) pConferenceVoid;
HRESULT             status;
H245_INST_T         H245Instance;
HQ931CALL           hQ931Call;
CC_HCONFERENCE      hConference;
HRESULT             SaveStatus;
CC_HCALL            hCall;

    H245Instance = pCall->H245Instance;
    hQ931Call    = pCall->hQ931Call;
    hConference  = pCall->hConference;
    FreeCall(pCall);

    if (H245Instance != H245_INVALID_ID)
        SaveStatus = H245ShutDown(H245Instance);
    else
        SaveStatus = H245_ERROR_OK;

    if (SaveStatus == H245_ERROR_OK) {
        SaveStatus = Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
        // Q931Hangup may legitimately return CS_BAD_PARAM, because the Q.931 call object
        // may have been deleted at this point
        if (SaveStatus == CS_BAD_PARAM)
            SaveStatus = CC_OK;
    } else
        Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);

    // Start up an enqueued call, if one exists
    for ( ; ; ) {
        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;
        }
    }
    UnlockConference(pConference);

    if (SaveStatus != CC_OK)
        status = SaveStatus;
    HResultLeave(status);
} // CancelCallConfirm()



HRESULT CancelCallReject    (void *pCallVoid, void *pConferenceVoid)
{
    // I don't care what the Gatekeeper says; I'm shutting down the call!
    return CancelCallConfirm(pCallVoid, pConferenceVoid);
} // CancelCallReject()

#endif



HRESULT OpenChannelConfirm  (DWORD hChannel)
{
HRESULT             status;
PCHANNEL            pChannel;
PCONFERENCE         pConference;
WORD                wNumCalls;
PCC_HCALL           CallList;
HRESULT             SaveStatus;
unsigned            i;
PCALL               pCall;

    status = LockChannelAndConference(hChannel, &pChannel, &pConference);
    if (status == CC_OK) {
        // Open a logical channel for each established call
        status = EnumerateCallsInConference(&wNumCalls, &CallList, pConference, ESTABLISHED_CALL);
        if (status == CC_OK) {
            SaveStatus = CC_OK;
            for (i = 0; i < wNumCalls; ++i) {
                if (LockCall(CallList[i], &pCall) == CC_OK) {
                    status = H245OpenChannel(pCall->H245Instance,       // H245 instance
                                             pChannel->hChannel,        // dwTransId
                                             pChannel->wLocalChannelNumber,
                                             pChannel->pTxH245TermCap,  // TxMode
                                             pChannel->pTxMuxTable,     // TxMux
                                             H245_INVALID_PORT_NUMBER,  // TxPort
                                             pChannel->pRxH245TermCap,  // RxMode
                                             pChannel->pRxMuxTable,     // RxMux
                                             pChannel->pSeparateStack);
                    if (status == H245_ERROR_OK)
                        (pChannel->wNumOutstandingRequests)++;
                    else
                        SaveStatus = status;
                    UnlockCall(pCall);
                }
            }

            if (CallList != NULL)
                Free(CallList);

            if (pChannel->wNumOutstandingRequests == 0) {
                // all open channel requests failed
                FreeChannel(pChannel);
            }
            else {
                UnlockChannel(pChannel);
            }

            if (SaveStatus != CC_OK)
                status = SaveStatus;
        }
        else {
            FreeChannel(pChannel);
        }
        UnlockConference(pConference);
    }


    HResultLeave(status);
} // OpenChannelConfirm()



HRESULT OpenChannelReject   (DWORD hChannel, HRESULT Reason)
{
PCHANNEL            pChannel;
PCONFERENCE         pConference;
CC_HCONFERENCE      hConference;
HRESULT             status;
CC_TX_CHANNEL_OPEN_CALLBACK_PARAMS Params = {0};

    status = LockChannelAndConference(hChannel, &pChannel, &pConference);
    if (status == CC_OK) {
        // Inform Call Control client of failure
        Params.hChannel         = hChannel;
        Params.pPeerRTPAddr     = pChannel->pPeerRTPAddr;
        Params.pPeerRTCPAddr    = pChannel->pPeerRTCPAddr;
        Params.dwRejectReason   = 0;
        Params.dwUserToken      = pChannel->dwUserToken;

        hConference = pConference->hConference;
        InvokeUserConferenceCallback(pConference,
                                     CC_TX_CHANNEL_OPEN_INDICATION,
                                     Reason,
                                     &Params);

        if (ValidateChannel(hChannel) == CC_OK)
            FreeChannel(pChannel);
        if (ValidateConference(hConference) == CC_OK)
            UnlockConference(pConference);
    }

    HResultLeave(status);
} // OpenChannelReject()



HRESULT AcceptChannelConfirm(DWORD hChannel)
{
HRESULT         status;
PCHANNEL        pChannel;
PCONFERENCE     pConference;
CC_HCONFERENCE  hConference;
PCALL           pCall;
unsigned        i;
H245_MUX_T      H245MuxTable;
CC_ACCEPT_CHANNEL_CALLBACK_PARAMS Params;

    status = LockChannelAndConference(hChannel, &pChannel, &pConference);
    if (status != CC_OK)
        HResultLeave(status);

    status = LockCall(pChannel->hCall, &pCall);
    if (status != CC_OK) {
        UnlockChannel(pChannel);
        UnlockConference(pConference);
        HResultLeave(status);
    }

    if (pChannel->wNumOutstandingRequests != 0) {
        PCC_ADDR pRTPAddr  = pChannel->pLocalRTPAddr;
        PCC_ADDR pRTCPAddr = pChannel->pLocalRTCPAddr;
        if ((pChannel->bMultipointChannel) &&
            (pConference->tsMultipointController == TS_TRUE)) {
            // Supply the RTP and RTCP addresses in the OpenLogicalChannelAck
            if (pConference->pSessionTable != NULL) {
                for (i = 0; i < pConference->pSessionTable->wLength; ++i) {
                    if (pConference->pSessionTable->SessionInfoArray[i].bSessionID ==
                        pChannel->bSessionID) {
                        pRTPAddr = pConference->pSessionTable->SessionInfoArray[i].pRTPAddr;
                        pRTCPAddr = pConference->pSessionTable->SessionInfoArray[i].pRTCPAddr;
                        break;
                    }
                }
            }
        }

        H245MuxTable.Kind = H245_H2250ACK;
        H245MuxTable.u.H2250ACK.nonStandardList = NULL;

        if (pRTPAddr != NULL) {
            if (pRTPAddr->bMulticast)
                H245MuxTable.u.H2250ACK.mediaChannel.type = H245_IP_MULTICAST;
            else
                H245MuxTable.u.H2250ACK.mediaChannel.type = H245_IP_UNICAST;
            H245MuxTable.u.H2250ACK.mediaChannel.u.ip.tsapIdentifier =
                pRTPAddr->Addr.IP_Binary.wPort;
            HostToH245IPNetwork(H245MuxTable.u.H2250ACK.mediaChannel.u.ip.network,
                                pRTPAddr->Addr.IP_Binary.dwAddr);
            H245MuxTable.u.H2250ACK.mediaChannelPresent = TRUE;
        } else
            H245MuxTable.u.H2250ACK.mediaChannelPresent = FALSE;

        if (pRTCPAddr != NULL) {
            if (pRTCPAddr->bMulticast)
                H245MuxTable.u.H2250ACK.mediaControlChannel.type = H245_IP_MULTICAST;
            else
                H245MuxTable.u.H2250ACK.mediaControlChannel.type = H245_IP_UNICAST;
            H245MuxTable.u.H2250ACK.mediaControlChannel.u.ip.tsapIdentifier =
                pRTCPAddr->Addr.IP_Binary.wPort;
            HostToH245IPNetwork(H245MuxTable.u.H2250ACK.mediaControlChannel.u.ip.network,
                                pRTCPAddr->Addr.IP_Binary.dwAddr);
            H245MuxTable.u.H2250ACK.mediaControlChannelPresent = TRUE;
        } else
            H245MuxTable.u.H2250ACK.mediaControlChannelPresent = FALSE;

        H245MuxTable.u.H2250ACK.dynamicRTPPayloadTypePresent = FALSE;
        H245MuxTable.u.H2250ACK.sessionIDPresent = TRUE;
        H245MuxTable.u.H2250ACK.sessionID = pChannel->bSessionID;

        status = H245OpenChannelAccept(pCall->H245Instance,
                                       0,                   // dwTransId
                                       pChannel->wRemoteChannelNumber, // Rx channel
                                       &H245MuxTable,
                                       0,                       // Tx channel
                                       NULL,                    // Tx mux
                                       H245_INVALID_PORT_NUMBER,// Port
                                       pChannel->pSeparateStack);
        if (status == CC_OK)
            pChannel->wNumOutstandingRequests = 0;
        else
            --(pChannel->wNumOutstandingRequests);
    }

    pChannel->tsAccepted = TS_TRUE;

    Params.hChannel = hChannel;
    if (status == CC_OK)
        UnlockChannel(pChannel);
    else
        FreeChannel(pChannel);
    UnlockCall(pCall);

    hConference = pConference->hConference;
    InvokeUserConferenceCallback(pConference,
                                 CC_ACCEPT_CHANNEL_INDICATION,
                                 status,
                                 &Params);
    if (ValidateConference(hConference) == CC_OK)
        UnlockConference(pConference);

    HResultLeave(status);
} // AcceptChannelConfirm(void()



HRESULT AcceptChannelReject (DWORD hChannel, HRESULT Reason)
{
HRESULT         status;
PCHANNEL        pChannel;
PCONFERENCE     pConference;
CC_HCONFERENCE  hConference;
CC_ACCEPT_CHANNEL_CALLBACK_PARAMS Params;

    status = LockChannelAndConference(hChannel, &pChannel, &pConference);
    if (status == CC_OK) {
        Params.hChannel = hChannel;
        FreeChannel(pChannel);

        hConference = pConference->hConference;
        InvokeUserConferenceCallback(pConference,
                                     CC_ACCEPT_CHANNEL_INDICATION,
                                     Reason,
                                     &Params);
        if (ValidateConference(hConference) == CC_OK)
            UnlockConference(pConference);
    }

    HResultLeave(status);
} // AcceptChannelReject()



//
// Handle gratuitous messages from Gatekeeper
//

// Note: pCall assumed locked when called!

HRESULT Disengage(void *pCallVoid)
{
CC_HCALL            hCall        = ((PCALL)pCallVoid)->hCall;
HRESULT             status;

    UnlockCall((PCALL)pCallVoid);
    status = ProcessRemoteHangup(hCall, CC_INVALID_HANDLE, CC_REJECT_GATEKEEPER_TERMINATED);
    HResultLeave(status);
} // Disengage()



// Note: pCall assumed locked when called!

HRESULT BandwidthShrunk(void *pCallVoid,
                        void *pConferenceVoid,
                        unsigned uBandwidthAllocated,
                        long lBandwidthChange)
{
PCALL               pCall       = (PCALL) pCallVoid;
PCONFERENCE         pConference = (PCONFERENCE)pConferenceVoid;
CC_BANDWIDTH_CALLBACK_PARAMS Params;

    Params.hCall = pCall->hCall;
    Params.dwBandwidthTotal  = uBandwidthAllocated;
    Params.lBandwidthChange  = lBandwidthChange;
    InvokeUserConferenceCallback(pConference,
                                 CC_BANDWIDTH_CHANGED_INDICATION,
                                 CC_OK,
                                 &Params);

    HResultLeave(CC_OK);
} // BandwidthShrunk()

#else  // GATEKEEPER
static char ch; // Kludge around warning C4206: nonstandard extension used : translation unit is empty
#endif // GATEKEEPER