/****************************************************************************
 *
 * $Archive:   S:\sturgeon\src\callcont\vcs\gkiman.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.77.1.6  $
 * $Date:   21 Aug 1997 16:56:32  $
 * $Author:   Helgebal  $
 *
 * Deliverable:
 *
 * Abstract:
 *
 * Notes:
 *
 *  Much effort has gone into working around the following constraints of
 *  the GKI interface:
 *  1) Only one admission request can be pending at a time. This is because
 *     the hCall is unknown until it completes.
 *  2) Only one bandwidth request per call can be pending at a time.
 *  3) Any pending bandwidth request must complete before issuing a
 *     disengage request.
 *  4) Any calls must be disengaged before issuing a deregistration request.
 *
 ***************************************************************************/
#ifdef GATEKEEPER

#pragma warning ( disable : 4115 4201 4214 4514)
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include "incommon.h"
#include "ccerror.h"
#include "isrg.h"
#include "gkiexp.h"
#include "callman2.h"
#ifdef FORCE_SERIALIZE_CALL_CONTROL
#include "cclock.h"
#endif // FORCE_SERIALIZE_CALL_CONTROL
#pragma warning ( default : 4115 4201 4214)

#if defined(DBG)

void GKDbgPrint(DWORD dwLevel,

#ifdef UNICODE_TRACE
				LPTSTR pszFormat,
#else
				LPSTR pszFormat,
#endif               
                ...);

#define GKDBG(_x_)  GKDbgPrint _x_

#else

#define GKDBG(_x_)

#endif

extern HINSTANCE        ghCallControlInstance;

#define Malloc(size) GlobalAlloc(GMEM_FIXED, size)
#define ReAlloc(p, size) GlobalReAlloc(p, GMEM_FIXED, size)
#define Free(p) GlobalFree(p)

#ifdef FORCE_SERIALIZE_CALL_CONTROL
#define EnterCallControlTop()      {CCLOCK_AcquireLock();}

#define LeaveCallControlTop(f)     {HRESULT stat; \
	                                stat = f; \
									CCLOCK_RelinquishLock(); \
                                    return stat;}
#else
#define EnterCallControlTop()      
#define LeaveCallControlTop(f)     {HRESULT stat; \
	                                stat = f; \
                                    return stat;}
#endif

#define DEFAULT_LISTEN_HANDLE   0xFFFFFFFF

#define GKIMAN_BASE             WM_USER

#define MIN_BANDWIDTH           1
#define MAX_BANDWIDTH           (0xFFFFFFFF / 100)

#define GKI_ADMITTING_HANDLE    ((HANDLE)-1)
#define GKI_BYPASS_HANDLE       ((HANDLE)-2)

// GKI Manager state
#define STATE_START                0
#define STATE_CLASS_REGISTERED     1
#define STATE_WINDOW_CREATED       2
#define STATE_REGISTERING          3
#define STATE_REGISTERING_REREG    4
#define STATE_REGISTERING_UNREG    5
#define STATE_REGISTERED           6
#define STATE_ADMITTING            7
#define STATE_ADMITTING_REREG      8
#define STATE_ADMITTING_UNREG      9
#define STATE_DISENGAGING         10
#define STATE_DISENGAGING_REREG   11
#define STATE_UNREGISTERING       12
#define STATE_UNREGISTERING_REREG 13
#define STATE_REG_BYPASS          14



typedef HRESULT (*PGKI_RegistrationRequest)(long             lVersion,
                                    SeqTransportAddr     *pCallSignalAddr, 
                                    EndpointType         *pTerminalType,
                                    SeqAliasAddr         *pAliasAddr, 
                                    HWND                 hWnd,
                                    WORD                 wBaseMessage,
                                    unsigned short       usRegistrationTransport /* = ipAddress_chosen */);

typedef HRESULT (*PGKI_UnregistrationRequest)(void);

typedef HRESULT (*PGKI_LocationRequest)(SeqAliasAddr         *pLocationInfo);

typedef HRESULT (*PGKI_AdmissionRequest)(unsigned short      usCallTypeChoice,
                                    SeqAliasAddr         *pDestinationInfo,
                                    TransportAddress     *pDestCallSignalAddress,
                                    SeqAliasAddr         *pDextExtraCallInfo,
                                    BandWidth            bandWidth,
                                    ConferenceIdentifier *pConferenceID,
                                    BOOL                 activeMC,
                                    BOOL                 answerCall,
                                    unsigned short       usCallTransport /* = ipAddress_chosen */);

typedef HRESULT (*PGKI_BandwidthRequest)(HANDLE              hModCall, 
                                    unsigned short       usCallTypeChoice,
                                    BandWidth            bandWidth);

typedef HRESULT (*PGKI_DisengageRequest)(HANDLE hCall);

typedef HRESULT (*PGKI_CleanupRequest)(void);

HRESULT Q931CopyAliasNames(PCC_ALIASNAMES *ppTarget, PCC_ALIASNAMES pSource);
HRESULT Q931FreeAliasNames(PCC_ALIASNAMES pSource);
#define CopyAliasNames Q931CopyAliasNames
#define FreeAliasNames Q931FreeAliasNames



typedef struct _LISTEN
{
  struct _LISTEN *  pNext;
  PCC_ALIASNAMES    pAliasNames;
  DWORD             hListen;
  DWORD             dwAddr;
  WORD              wPort;
} LISTEN, *PLISTEN;

//
// GKI Manager Global Data
//
CRITICAL_SECTION  GkiLock;
const char      szClassName[]         = "GkiManWndClass";
HWND            hwndGki               = 0;
ATOM            atomGki               = 0;
unsigned int    uGkiState             = STATE_START;
PLISTEN         pListenList           = NULL;
unsigned int    uGkiCalls             = 0;
unsigned int    uPendingDisengages    = 0;

HINSTANCE				   hGkiDll					  = 0;
PGKI_RegistrationRequest   pGKI_RegistrationRequest   = NULL;
PGKI_UnregistrationRequest pGKI_UnregistrationRequest = NULL;
PGKI_LocationRequest       pGKI_LocationRequest       = NULL;
PGKI_AdmissionRequest      pGKI_AdmissionRequest      = NULL;
PGKI_BandwidthRequest      pGKI_BandwidthRequest      = NULL;
PGKI_DisengageRequest      pGKI_DisengageRequest      = NULL;
PGKI_CleanupRequest		   pGKI_CleanupRequest        = NULL;


HRESULT ValidateCall(DWORD hCall);
HRESULT	LastGkiError = CC_GKI_STATE;

//
// Forward declarations
//
LONG APIENTRY GkiWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam);



//
// Helper subroutines
//

#ifdef    DBG

typedef struct _GKIMAP
{
  HRESULT       hResult;
  char *        pString;
} GKIMAP;

GKIMAP GkiErrorNames[] =
{
  GKI_OK,               "GKI_OK",
  GKI_EXIT_THREAD,      "GKI_EXIT_THREAD",
  GKI_REDISCOVER,       "GKI_REDISCOVER",
  GKI_DELETE_CALL,      "GKI_DELETE_CALL",
  GKI_GCF_RCV,          "GKI_GCF_RCV",
  GKI_NO_MEMORY,        "GKI_NO_MEMORY",
  GKI_NO_THREAD,        "GKI_NO_THREAD",
  GKI_HANDLE_ERROR,     "GKI_HANDLE_ERROR",
  GKI_ALREADY_REG,      "GKI_ALREADY_REG",
  GKI_VERSION_ERROR,    "GKI_VERSION_ERROR",
  GKI_ENCODER_ERROR,    "GKI_ENCODER_ERROR",
  GKI_NOT_REG,          "GKI_NOT_REG",
  GKI_BUSY,             "GKI_BUSY",
  GKI_NO_TA_ERROR,      "GKI_NO_TA_ERROR",
  GKI_NO_RESPONSE,      "GKI_NO_RESPONSE",
  GKI_DECODER_ERROR,    "GKI_DECODER_ERROR",
};

char *StateNames[] =
{
  "STATE_START",
  "STATE_CLASS_REGISTERED",
  "STATE_WINDOW_CREATED",
  "STATE_REGISTERING",
  "STATE_REGISTERING_REREG",
  "STATE_REGISTERING_UNREG",
  "STATE_REGISTERED",
  "STATE_ADMITTING",
  "STATE_ADMITTING_REREG",
  "STATE_ADMITTING_UNREG",
  "STATE_DISENGAGING",
  "STATE_DISENGAGING_REREG",
  "STATE_UNREGISTERING",
  "STATE_UNREGISTERING_REREG",
  "STATE_REG_BYPASS",
};

char *CallStateNames[] =
{
  "GCS_START",
  "GCS_WAITING",
  "GCS_ADMITTING",
  "GCS_ADMITTING_CLOSE_PENDING",
  "GCS_ADMITTED",
  "GCS_CHANGING",
  "GCS_CHANGING_CLOSE_PENDING",
  "GCS_DISENGAGING",
};

char szBuffer[128];

char * GkiErrorName(char *szFormat, HRESULT hResult)
{
  register int  nIndex = sizeof(GkiErrorNames) / sizeof(GkiErrorNames[0]);
  char          szTemp[32];

  while (nIndex > 0)
  {
    if (GkiErrorNames[--nIndex].hResult == hResult)
    {
      wsprintf(szBuffer, szFormat, GkiErrorNames[nIndex].pString);
      return szBuffer;
    }
  }

  wsprintf(szTemp, "Unknown(0x%x)", hResult);
  wsprintf(szBuffer, szFormat, szTemp);
  return szBuffer;
} // GkiErrorName()

char * StateName(char *szFormat, unsigned uState)
{
  char szTemp[32];
  if (uState < (sizeof(StateNames)/sizeof(StateNames[0])))
  {
    wsprintf(szBuffer, szFormat, StateNames[uState]);
  }
  else
  {
    wsprintf(szTemp, "Unknown(%d)", uState);
    wsprintf(szBuffer, szFormat, szTemp);
  }
  return szBuffer;
} // StateName()

char * CallStateName(char *szFormat, unsigned uCallState)
{
  char szTemp[32];
  if (uCallState <= (sizeof(CallStateNames)/sizeof(CallStateNames[0])))
  {
    wsprintf(szBuffer, szFormat, CallStateNames[uCallState]);
  }
  else
  {
    wsprintf(szTemp, "Unknown(%d)", uCallState);
    wsprintf(szBuffer, szFormat, szTemp);
  }
  return szBuffer;
} // CallStateName()

#else

#define GkiErrorName(x,y)   ""
#define StateName(x,y)      ""
#define CallStateName(x,y)  ""

#endif // DBG



HRESULT MapRegistrationRejectReason(register UINT uReason)
{
  register HRESULT lReason;

  // TBD - Map reason code into CC_xxx HRESULT
  switch (uReason)
  {
  case discoveryRequired_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case RgstrtnRjctRsn_invldRvsn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case invalidCallSignalAddress_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case invalidRASAddress_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case duplicateAlias_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case invalidTerminalType_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case RgstrtnRjctRsn_undfndRsn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case transportNotSupported_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  default:
    lReason = CC_GATEKEEPER_REFUSED;
  } // switch

  return lReason;
} // MapRegistrationRejectReason()



HRESULT MapAdmissionRejectReason(register UINT uReason)
{
  register HRESULT lReason;

  // TBD - Map reason code into CC_xxx HRESULT
  switch (uReason)
  {
  case calledPartyNotRegistered_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case ARRn_invldPrmssn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case AdmssnRjctRsn_rqstDnd_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case AdmssnRjctRsn_undfndRsn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case callerNotRegistered_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case routeCallToGatekeeper_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case invldEndpntIdntfr_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case AdmssnRjctRsn_rsrcUnvlbl_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  default:
    lReason = CC_GATEKEEPER_REFUSED;
  } // switch

  return lReason;
} // MapAdmissionRejectReason()



HRESULT MapBandwidthRejectReason(register UINT uReason)
{
  register HRESULT lReason;

  // TBD - Map reason code into CC_xxx HRESULT
  switch (uReason)
  {
  case notBound_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case invalidConferenceID_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case BndRjctRsn_invldPrmssn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case insufficientResources_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case BndRjctRsn_invldRvsn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  case BndRjctRsn_undfndRsn_chosen:
    lReason = CC_GATEKEEPER_REFUSED;
    break;
  default:
    lReason = CC_GATEKEEPER_REFUSED;
  } // switch

  return lReason;
} // MapBandwidthRejectReason()



/*
 *  NOTES
 *    GkiLock must be locked before calling this routine!
 */

static PLISTEN ListenEnqueue(register PLISTEN pListen)
{
  if (pListenList && pListenList->hListen == DEFAULT_LISTEN_HANDLE)
  {
    PLISTEN pDefaultListen = pListenList;
    pListenList = pListenList->pNext;
    Free(pDefaultListen);
  }
  pListen->pNext = pListenList;
  return pListenList = pListen;
} // ListenEnqueue()



/*
 *  NOTES
 *    GkiLock must be locked before calling this routine!
 */

static PLISTEN ListenDequeue(register DWORD hListen)
{
  register PLISTEN      pListen = pListenList;
  register PLISTEN      pListenPrev;

  if (pListen)
  {
    if (pListen->hListen == hListen)
    {
      pListenList = pListen->pNext;
    }
    else
    {
      do
      {
        pListenPrev = pListen;
        pListen = pListen->pNext;
      } while (pListen && pListen->hListen != hListen);
      if (pListen)
      {
        pListenPrev->pNext = pListen->pNext;
      }
    }
  }

  return pListen;
} // ListenDequeue()



/*
 *  NOTES
 *    Since the pGkiCall is locked, we don't need a critical section
 *    around the queue manipulation code.
 */

static PBWREQ BwReqEnqueue(register PGKICALL pGkiCall, register PBWREQ pBwReq)
{
  pBwReq->pNext = NULL;
  if (pGkiCall->pBwReqHead)
  {
    pGkiCall->pBwReqTail->pNext = pBwReq;
  }
  else
  {
    pGkiCall->pBwReqHead = pBwReq;
  }
  return pGkiCall->pBwReqTail = pBwReq;
} // BwReqEnqueue()



/*
 *  NOTES
 *    Since the pGkiCall is locked, we don't need a critical section
 *    around the queue manipulation code.
 */

static PBWREQ BwReqDequeue(register PGKICALL pGkiCall)
{
  register PBWREQ pBwReq = pGkiCall->pBwReqHead;
  if (pBwReq)
  {
    pGkiCall->pBwReqHead = pBwReq->pNext;
  }
  return pBwReq;
} // BwReqDequeue()



DWORD GetIpAddress(void)
{
  DWORD dwAddr;
  char szHostName[128];
  if (gethostname(szHostName, sizeof(szHostName)) == 0)
  {
    struct hostent *pHostent;
    pHostent = gethostbyname(szHostName);
    if (pHostent != NULL)
    {
      ASSERT(pHostent->h_addrtype == AF_INET);
      dwAddr = *((DWORD *)pHostent->h_addr_list[0]);
      return ntohl(dwAddr);
    }
  }

  return INADDR_ANY;
} // GetIpAddress()



// Caveat: *pAlias should be initialized to all 0 before calling!

static HRESULT CopyAliasItem(SeqAliasAddr *pAlias, PCC_ALIASITEM pAliasItem)
{
  unsigned int uDigit;
  unsigned int uPrefixLength;
  unsigned int uDataLength;

  if (pAliasItem->pData == NULL || pAliasItem->wDataLength == 0)
    return CC_BAD_PARAM;

  if (pAliasItem->pPrefix)
  {
    // Strip off terminating NULs if included in prefix length
    uPrefixLength = pAliasItem->wPrefixLength;
    while (uPrefixLength && pAliasItem->pPrefix[uPrefixLength - 1] == 0)
      --uPrefixLength;
  }
  else
  {
    uPrefixLength = 0;
  }

  uDataLength = pAliasItem->wDataLength;

  switch (pAliasItem->wType)
  {
  case CC_ALIAS_H323_ID:
    pAlias->value.choice = h323_ID_chosen;
    pAlias->value.u.h323_ID.value = Malloc((uPrefixLength + uDataLength) * sizeof(pAliasItem->pData[0]));
    if (pAlias->value.u.h323_ID.value == NULL)
    {
      GKDBG((1, "CopyAliasItem: Could not allocate %d bytes memory",
               (uPrefixLength + uDataLength) * sizeof(pAliasItem->pData[0])));
      return CC_NO_MEMORY;
    }
    if (uPrefixLength)
    {
      memcpy(&pAlias->value.u.h323_ID.value[0],
             pAliasItem->pPrefix,
             uPrefixLength * sizeof(pAliasItem->pPrefix[0]));
      memcpy(&pAlias->value.u.h323_ID.value[uPrefixLength],
             pAliasItem->pData,
             uDataLength * sizeof(pAliasItem->pData[0]));
    }
    else
    {
      memcpy(&pAlias->value.u.h323_ID.value[0],
             pAliasItem->pData,
             uDataLength * sizeof(pAliasItem->pData[0]));
    }
    pAlias->value.u.h323_ID.length = (unsigned short)(uPrefixLength + uDataLength);
    break;

  case CC_ALIAS_H323_PHONE:
    pAlias->value.choice = e164_chosen;
    if (uPrefixLength)
    {
      for (uDigit = 0; uDigit < uPrefixLength; ++uDigit)
      {
        pAlias->value.u.e164[uDigit] = (char)pAliasItem->pPrefix[uDigit];
      }
      for (uDigit = 0; uDigit < uDataLength; ++uDigit)
      {
        pAlias->value.u.e164[uDigit + uPrefixLength] = (char)pAliasItem->pData[uDigit];
      }
    }
    else
    {
      for (uDigit = 0; uDigit < uDataLength; ++uDigit)
      {
        pAlias->value.u.e164[uDigit] = (char)pAliasItem->pData[uDigit];
      }
    }
    break;

  default:
    GKDBG((1, "CopyAliasItem: Bad alias name type %d", pAliasItem->wType));
    return CC_BAD_PARAM;
  } // switch

  return NOERROR;
} // CopyAliasItem()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static void GkiAllocCall(PGKICALL pGkiCall, HANDLE hGkiCall)
{
  ASSERT(pGkiCall != NULL);
  ASSERT(hGkiCall != 0);
  ASSERT(hGkiCall != GKI_ADMITTING_HANDLE);
  pGkiCall->hGkiCall = hGkiCall;
  pGkiCall->uGkiCallState = GCS_ADMITTED;
  ++uGkiCalls;
  GKDBG((1, "GkiAllocCall: uGkiCalls = %d", uGkiCalls));
} // GkiAllocCall()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static HRESULT GkiCancelCall(PGKICALL pGkiCall, void *pConference)
{
  DWORD                 hCall;

  ASSERT(pGkiCall != NULL);
  pConference = pConference;            // Disable compiler warning
  hCall = pGkiCall->hCall;

  GKDBG((1, CallStateName("GkiCancelCall <- Call State = %s", pGkiCall->uGkiCallState), 0));

  switch (pGkiCall->uGkiCallState)
  {
  case GCS_START:
    break;

  case GCS_WAITING:
    ASSERT(pGkiCall->hGkiCall == 0);
    if (pGkiCall->bAnswerCall)
      AcceptCallReject(pGkiCall->pCall, pConference, CC_GATEKEEPER_REFUSED);
    else
      PlaceCallReject (pGkiCall->pCall, pConference, CC_GATEKEEPER_REFUSED);
    break;

  case GCS_ADMITTING:
  case GCS_ADMITTING_CLOSE_PENDING:
    ASSERT(pGkiCall->hGkiCall == GKI_ADMITTING_HANDLE);
    if (pGkiCall->bAnswerCall)
      AcceptCallReject(pGkiCall->pCall, pConference, CC_GATEKEEPER_REFUSED);
    else
      PlaceCallReject (pGkiCall->pCall, pConference, CC_GATEKEEPER_REFUSED);
    break;

  case GCS_ADMITTED:
  case GCS_CHANGING:
  case GCS_CHANGING_CLOSE_PENDING:
  case GCS_DISENGAGING:
    ASSERT(pGkiCall->hGkiCall != 0);
    ASSERT(pGkiCall->hGkiCall != GKI_ADMITTING_HANDLE);
    Disengage(pGkiCall->pCall);
    return NOERROR;

  default:
    GKDBG((1, "GkiCancelCall: Invalid call state %d", pGkiCall->uGkiCallState));
  } // switch

  if (ValidateCall(hCall) == NOERROR && pGkiCall->uGkiCallState != GCS_START)
  {
    GkiFreeCall(pGkiCall);
  }

  GKDBG((1, CallStateName("GkiCancelCall -> Call State = %s", pGkiCall->uGkiCallState), 0));
  return NOERROR;
} // GkiCancelCall()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static HRESULT GkiCancelAdmitting(PGKICALL pGkiCall, void *pConference)
{
  ASSERT(pGkiCall != NULL);
  pConference = pConference;            // Disable compiler warning

  GKDBG((1, CallStateName("GkiCancelAdmitting <- Call State = %s", pGkiCall->uGkiCallState), 0));

  switch (pGkiCall->uGkiCallState)
  {
  case GCS_ADMITTING:
    ASSERT(pGkiCall->hGkiCall == GKI_ADMITTING_HANDLE);
    pGkiCall->hGkiCall = 0;
    pGkiCall->uGkiCallState = GCS_WAITING;
    break;

  case GCS_ADMITTING_CLOSE_PENDING:
    ASSERT(pGkiCall->hGkiCall == GKI_ADMITTING_HANDLE);
    GkiFreeCall(pGkiCall);
    break;

  } // switch

  GKDBG((1, CallStateName("GkiCancelAdmitting -> Call State = %s", pGkiCall->uGkiCallState), 0));
  return NOERROR;
} // GkiCancelAdmitting()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static HRESULT CheckPendingBandwidth(PGKICALL pGkiCall);

static HRESULT GatekeeperNotFound(PGKICALL pGkiCall, void *pConference)
{
  ASSERT(pGkiCall != NULL);
  ASSERT(pConference != NULL);

  GKDBG((1, CallStateName("GatekeeperNotFound <- Call State = %s", pGkiCall->uGkiCallState), 0));

  switch (pGkiCall->uGkiCallState)
  {
  case GCS_START:
  case GCS_ADMITTED:
    break;

  case GCS_WAITING:
  case GCS_ADMITTING:
    GkiOpenCall(pGkiCall, pConference);
    break;

  case GCS_ADMITTING_CLOSE_PENDING:
  case GCS_CHANGING_CLOSE_PENDING:
  case GCS_DISENGAGING:
    GkiCloseCall(pGkiCall);
    break;

  case GCS_CHANGING:
    pGkiCall->uGkiCallState = GCS_ADMITTED;
    pGkiCall->uBandwidthAllocated = MAX_BANDWIDTH;
    CheckPendingBandwidth(pGkiCall);
    break;

  default:
    GKDBG((1, "GatekeeperNotFound: Invalid call state %d", pGkiCall->uGkiCallState));
  } // switch

  GKDBG((1, CallStateName("GatekeeperNotFound -> Call State = %s", pGkiCall->uGkiCallState), 0));
  return NOERROR;
} // GatekeeperNotFound()



/*
 *  NOTES
 *    GkiLock must be locked before calling this routine!
 */

static HRESULT GkiRegister(void)
{
  register HRESULT      status = NOERROR;

  ASSERT(pListenList != NULL);

  switch (uGkiState)
  {
  case STATE_START:
    // Register window class
    {
      WNDCLASS wndclass = { 0, GkiWndProc, 0, 0, 0, 0, 0, 0, NULL, szClassName };
      atomGki = RegisterClass(&wndclass);
      if (atomGki == 0)
      {
        status = HRESULT_FROM_WIN32(GetLastError());
        GKDBG((1, "GkiRegister: Error 0x%x registering class", status));
        break;
      }
    }
    uGkiState = STATE_CLASS_REGISTERED;

  // Fall-through to next case

  case STATE_CLASS_REGISTERED:
    // Create window to receive GKI messages
    hwndGki = CreateWindow(szClassName, "", WS_OVERLAPPED, 0, 0, 0, 0, 0, 0, 0, NULL);
    if (hwndGki == 0)
    {
      status = HRESULT_FROM_WIN32(GetLastError());
      GKDBG((1, "GkiRegister: Error 0x%x creating window", status));
      break;
    }
    uGkiState = STATE_WINDOW_CREATED;

    // Fall-through to next case

  case STATE_WINDOW_CREATED:
    {
      PLISTEN           pListen;
      unsigned          uListens    = 0;
      unsigned          uAliasNames = 0;
      SeqTransportAddr *pTransportAddrs;
      SeqAliasAddr     *pAliasAddrs;
      PCC_ALIASITEM     pAliasItem;
      unsigned          uIndex;
      unsigned          uDigit;
      EndpointType      TerminalType = {0};

      // Count Transport Addresses and Alias Names
      pListen = pListenList;
      while (pListen)
      {
        // Count the Transport Address
        ++uListens;

        if (pListen->pAliasNames)
        {
          // Count the Alias Names
          uAliasNames += pListen->pAliasNames->wCount;
        }
        pListen = pListen->pNext;
      }

      pTransportAddrs = Malloc(uListens * sizeof(*pTransportAddrs));
      if (pTransportAddrs == NULL)
      {
        GKDBG((1, "GkiRegister: Could not allocate %d Transport Addresses", uListens));
        return CC_NO_MEMORY;
      }

      if (uAliasNames)
      {
        pAliasAddrs = Malloc(uAliasNames * sizeof(*pAliasAddrs));
        if (pAliasAddrs == NULL)
        {
          Free(pTransportAddrs);
          GKDBG((1, "GkiRegister: Could not allocate %d Alias Addresses", uAliasNames));
          return CC_NO_MEMORY;
        }
      }
      else
      {
        pAliasAddrs = NULL;
      }

      pListen     = pListenList;
      uListens    = 0;
      uAliasNames = 0;
      while (pListen)
      {
        // Initialize a transport address
        // TBD - throw out duplicates
        pTransportAddrs[uListens].next = &pTransportAddrs[uListens + 1];
        pTransportAddrs[uListens].value.choice = ipAddress_chosen;
        pTransportAddrs[uListens].value.u.ipAddress.ip.length = 4;
        *((DWORD *)pTransportAddrs[uListens].value.u.ipAddress.ip.value) = pListen->dwAddr;
        pTransportAddrs[uListens].value.u.ipAddress.port = pListen->wPort;

        // Add any alias names to list
        // TBD - throw out duplicates
        if (pListen->pAliasNames)
        {
          pAliasItem = pListen->pAliasNames->pItems;
          for (uIndex = 0; uIndex < pListen->pAliasNames->wCount; ++uIndex, ++pAliasItem)
          {
            pAliasAddrs[uAliasNames].next = &pAliasAddrs[uAliasNames + 1];
            switch (pAliasItem->wType)
            {
            case CC_ALIAS_H323_ID:
              pAliasAddrs[uAliasNames].value.choice = h323_ID_chosen;
              pAliasAddrs[uAliasNames].value.u.h323_ID.length = pAliasItem->wDataLength;
              pAliasAddrs[uAliasNames].value.u.h323_ID.value  = pAliasItem->pData;
              break;

            case CC_ALIAS_H323_PHONE:
              pAliasAddrs[uAliasNames].value.choice = e164_chosen;
              memset(pAliasAddrs[uAliasNames].value.u.e164, 0, sizeof(pAliasAddrs[uAliasNames].value.u.e164));
              for (uDigit = 0; uDigit < pAliasItem->wDataLength; ++uDigit)
              {
                pAliasAddrs[uAliasNames].value.u.e164[uDigit] = (char)pAliasItem->pData[uDigit];
              }
              break;

            default:
              Free(pAliasAddrs);
              Free(pTransportAddrs);
              GKDBG((1, "GkiRegister: Bad alias name type %d",
                      pAliasItem->wType));
              return CC_BAD_PARAM;
            } // switch
            ++uAliasNames;
          } // for
        } // if
        ++uListens;
        pListen = pListen->pNext;
      } // while
      pTransportAddrs[uListens - 1].next = NULL;
      if (pAliasAddrs)
      {
        pAliasAddrs[uAliasNames - 1].next = NULL;
      }

      // Initialize TerminalType
      TerminalType.bit_mask = terminal_present;
      TerminalType.mc       = TRUE;

      uGkiState = STATE_REGISTERING;
      GKDBG((1, "GKI_RegistrationRequest called...", 0));
      status =
        pGKI_RegistrationRequest(GKI_VERSION,       // lVersion
                                 pTransportAddrs,   // pCallSignalAddr
                                 &TerminalType,     // pTerminalType
                                 pAliasAddrs,       // pRgstrtnRgst_trmnlAls
                                 hwndGki,           // hWnd
                                 GKIMAN_BASE,       // wBaseMessage
                                 ipAddress_chosen); // usRegistrationTransport
      if (status == NOERROR)
      {
        GKDBG((1, GkiErrorName("GKI_RegistrationRequest returned %s", status), 0));
      }
      else
      {
        GKDBG((1, GkiErrorName("GKI_RegistrationRequest returned %s", status), 0));
        uGkiState = STATE_WINDOW_CREATED;
      }
      if (pAliasAddrs)
        Free(pAliasAddrs);
      if (pTransportAddrs)
        Free(pTransportAddrs);
    }
    break;

  case STATE_REGISTERING:
  case STATE_REGISTERING_REREG:
  case STATE_REGISTERING_UNREG:
    uGkiState = STATE_REGISTERING_REREG;
    break;

  case STATE_REGISTERED:
    uGkiState = STATE_UNREGISTERING_REREG;
    GKDBG((1, "GKI_UnregistrationRequest called...", 0));
    status = pGKI_UnregistrationRequest();
    if (status == NOERROR)
    {
      GKDBG((1, GkiErrorName("GKI_UnregistrationRequest returned %s", status), 0));
    }
    else
    {
      GKDBG((1, GkiErrorName("GKI_UnregistrationRequest returned %s", status), 0));
      uGkiState = STATE_REG_BYPASS;
      ApplyToAllCalls(GatekeeperNotFound);
    }
    break;

  case STATE_ADMITTING:
  case STATE_ADMITTING_REREG:
  case STATE_ADMITTING_UNREG:
    uGkiState = STATE_ADMITTING_REREG;
    break;

  case STATE_DISENGAGING:
    uGkiState = STATE_DISENGAGING_REREG;
    break;

  case STATE_DISENGAGING_REREG:
    break;

  case STATE_UNREGISTERING:
    uGkiState = STATE_UNREGISTERING_REREG;
    break;

  case STATE_UNREGISTERING_REREG:
    break;

  case STATE_REG_BYPASS:
    break;

  default:
    GKDBG((1, "GkiRegister: Invalid state %d", uGkiState));
    status = LastGkiError;
  } // switch

  return status;
} // GkiRegister()



/*
 *  NOTES
 *    GkiLock must be locked before calling this routine!
 */

HRESULT GkiCloseCallNoError(PGKICALL pGkiCall, void *pConference)
{
  ASSERT(pGkiCall != NULL);
  pConference = pConference;            // Disable compiler warning
  if (pGkiCall->uGkiCallState != GCS_START)
    GkiCloseCall(pGkiCall);
  return NOERROR;
} // GkiCloseCallNoError()

HRESULT GkiUnregister(void)
{
  register HRESULT      status = NOERROR;

  switch (uGkiState)
  {
  case STATE_REG_BYPASS:
    ApplyToAllCalls(GkiCancelCall);
    uGkiState = STATE_WINDOW_CREATED;
    break;

  case STATE_UNREGISTERING_REREG:
    uGkiState = STATE_UNREGISTERING;
    break;

  case STATE_UNREGISTERING:
    break;

  case STATE_DISENGAGING_REREG:
    if (uGkiCalls != 0 || uPendingDisengages != 0)
    {
      uGkiState = STATE_DISENGAGING;
    }
    else
    {
      uGkiState = STATE_REGISTERED;
      return GkiUnregister();
    }
    break;

  case STATE_DISENGAGING:
    if (uGkiCalls == 0 && uPendingDisengages == 0)
    {
      uGkiState = STATE_REGISTERED;
      return GkiUnregister();
    }
    break;

  case STATE_ADMITTING_UNREG:
  case STATE_ADMITTING_REREG:
  case STATE_ADMITTING:
    uGkiState = STATE_ADMITTING_UNREG;
    break;

  case STATE_REGISTERING_UNREG:
  case STATE_REGISTERING_REREG:
  case STATE_REGISTERING:
    uGkiState = STATE_REGISTERING_UNREG;
    break;

  case STATE_REGISTERED:
    if (uGkiCalls)
    {
      // Issue Disengage Request for every call
      uGkiState = STATE_DISENGAGING;
      ApplyToAllCalls(GkiCloseCallNoError);
      break;

    }
    else
    {
      // Issue Unregistration Request
      uGkiState = STATE_UNREGISTERING;
      GKDBG((1, "GKI_UnregistrationRequest called...", 0));
      status = pGKI_UnregistrationRequest();
      if (status == NOERROR)
      {
        GKDBG((1, GkiErrorName("GKI_UnregistrationRequest returned %s", status), 0));
      }
      else
      {
        GKDBG((1, GkiErrorName("GKI_UnregistrationRequest returned %s", status), 0));
        uGkiState = STATE_WINDOW_CREATED;
      }
    }
    break;

  case STATE_WINDOW_CREATED:
  case STATE_CLASS_REGISTERED:
  case STATE_START:
    GKDBG((1, StateName("GkiUnregister: Already in uninitialized state %s", uGkiState), 0));
    status = LastGkiError;
    break;

  default:
    GKDBG((1, "GkiUnregister: Invalid state %d", uGkiState));
    status = LastGkiError;
  } // switch

  return status;
} // GkiUnregister()



void DeInitGkiManager(void)
{
  register PLISTEN      pListen;

  EnterCriticalSection(&GkiLock);

#if 0
  // TBD - When called from DllMain PROCESS_DETACH, this does not work because
  // apparently the socket to the Gatekeeper has already been closed.
  if (uGkiState != STATE_START)
  {
    GkiUnregister();
    uGkiState = STATE_START;
  }
#else
  uGkiState = STATE_START;
#endif

  while (pListenList)
  {
    pListen = pListenList;
    pListenList = pListenList->pNext;
    if (pListen->pAliasNames)
    {
      FreeAliasNames(pListen->pAliasNames);
    }
    Free(pListen);
  }

  pGKI_RegistrationRequest   = NULL;
  pGKI_UnregistrationRequest = NULL;
  pGKI_LocationRequest       = NULL;
  pGKI_AdmissionRequest      = NULL;
  pGKI_BandwidthRequest      = NULL;
  pGKI_DisengageRequest      = NULL;
  if (hGkiDll)
  {
	if (pGKI_CleanupRequest)
          pGKI_CleanupRequest();

#if defined(REMOVE_FROM_TSP)
    FreeLibrary(hGkiDll);
#endif // REMOVE_FROM_TSP
  }
  pGKI_CleanupRequest = NULL;

  LeaveCriticalSection(&GkiLock);
  DeleteCriticalSection(&GkiLock);
  DestroyWindow(hwndGki);
  UnregisterClass(szClassName, 0);
} // DeInitGkiManager()



HRESULT InitGkiManager(void)
{
  InitializeCriticalSection(&GkiLock);

#if defined(REMOVE_FROM_TSP)

  hGkiDll = LoadLibrary("GKI.DLL");
  if (hGkiDll == NULL)
  {
    DeInitGkiManager();
  	return CC_GKI_LOAD;
  }

  pGKI_RegistrationRequest   = (PGKI_RegistrationRequest)  GetProcAddress(hGkiDll, "GKI_RegistrationRequest");
  pGKI_UnregistrationRequest = (PGKI_UnregistrationRequest)GetProcAddress(hGkiDll, "GKI_UnregistrationRequest");
  pGKI_LocationRequest       = (PGKI_LocationRequest)      GetProcAddress(hGkiDll, "GKI_LocationRequest");
  pGKI_AdmissionRequest      = (PGKI_AdmissionRequest)     GetProcAddress(hGkiDll, "GKI_AdmissionRequest");
  pGKI_BandwidthRequest      = (PGKI_BandwidthRequest)     GetProcAddress(hGkiDll, "GKI_BandwidthRequest");
  pGKI_DisengageRequest      = (PGKI_DisengageRequest)     GetProcAddress(hGkiDll, "GKI_DisengageRequest");
  pGKI_CleanupRequest        = (PGKI_CleanupRequest)       GetProcAddress(hGkiDll, "GKI_CleanupRequest");
  
  if (pGKI_RegistrationRequest   == NULL ||
      pGKI_UnregistrationRequest == NULL ||
      pGKI_LocationRequest       == NULL ||
      pGKI_AdmissionRequest      == NULL ||
      pGKI_BandwidthRequest      == NULL ||
      pGKI_DisengageRequest      == NULL ||
	  pGKI_CleanupRequest        == NULL)
  {
    DeInitGkiManager();
  	return CC_GKI_LOAD;
  }

#else  // REMOVE_FROM_TSP

  pGKI_RegistrationRequest   = GKI_RegistrationRequest;
  pGKI_UnregistrationRequest = GKI_UnregistrationRequest;
  pGKI_LocationRequest       = GKI_LocationRequest;
  pGKI_AdmissionRequest      = GKI_AdmissionRequest;
  pGKI_BandwidthRequest      = GKI_BandwidthRequest;
  pGKI_DisengageRequest      = GKI_DisengageRequest;
  pGKI_CleanupRequest        = GKI_CleanupRequest;

#endif // REMOVE_FROM_TSP

  return NOERROR;
} // InitGkiManager()



//
// Entry Points
//

HRESULT GkiFreeCall(PGKICALL pGkiCall)
{
  HRESULT               status = NOERROR;

  ASSERT(pGkiCall != NULL);
  ASSERT(pGkiCall->uGkiCallState != GCS_START);
  pGkiCall->hGkiCall = 0;

  while (pGkiCall->pBwReqHead)
  {
    Free(BwReqDequeue(pGkiCall));
  }

  if (pGkiCall->pCalleeAliasNames)
  {
	Q931FreeAliasNames(pGkiCall->pCalleeAliasNames);
    pGkiCall->pCalleeAliasNames = NULL;
  }

  if (pGkiCall->pCalleeExtraAliasNames != NULL)
  {
	Q931FreeAliasNames(pGkiCall->pCalleeExtraAliasNames);
    pGkiCall->pCalleeExtraAliasNames = NULL;
  }

  switch (pGkiCall->uGkiCallState)
  {
  case GCS_START:
  case GCS_WAITING:
    break;

  case GCS_ADMITTING:
    ASSERT(uGkiState == STATE_ADMITTING);
    switch (uGkiState)
    {
    case STATE_ADMITTING:
      uGkiState = STATE_REGISTERED;
      break;
    } // switch
    break;

  case GCS_ADMITTING_CLOSE_PENDING:
    ASSERT(uGkiState == STATE_ADMITTING || uGkiState == STATE_ADMITTING_UNREG || uGkiState == STATE_ADMITTING_REREG);
    switch (uGkiState)
    {
    case STATE_ADMITTING:
      uGkiState = STATE_REGISTERED;
      break;
    case STATE_ADMITTING_UNREG:
      uGkiState = STATE_REGISTERED;
      status = GkiUnregister();
      break;
    case STATE_ADMITTING_REREG:
      uGkiState = STATE_REGISTERED;
      status = GkiRegister();
      break;
    } // switch
    break;

  case GCS_ADMITTED:
  case GCS_CHANGING:
  case GCS_CHANGING_CLOSE_PENDING:
  case GCS_DISENGAGING:
    --uGkiCalls;
    GKDBG((1, "GkiFreeCall: uGkiCalls = %d", uGkiCalls));
    break;

  default:
    GKDBG((1, "GkiFreeCall: Invalid call state %d", pGkiCall->uGkiCallState));
  } // switch

  pGkiCall->uGkiCallState = GCS_START;

  if (uGkiCalls == 0 && uPendingDisengages == 0)
  {
    switch (uGkiState)
    {
    case STATE_DISENGAGING:
      uGkiState = STATE_REGISTERED;
      status = GkiUnregister();
      break;
    case STATE_DISENGAGING_REREG:
      uGkiState = STATE_REGISTERED;
      status = GkiRegister();
      break;
    } // switch
    if (pListenList && pListenList->hListen == DEFAULT_LISTEN_HANDLE)
    {
      GkiCloseListen(DEFAULT_LISTEN_HANDLE);
    }
  }

  return status;
} // GkiFreeCall()



HRESULT GkiCloseListen  (DWORD hListen)
{
  register PLISTEN      pListen;
  register HRESULT      status;

  GKDBG((1, StateName("GkiCloseListen <- State = %s", uGkiState), 0));
  EnterCriticalSection(&GkiLock);

  pListen = ListenDequeue(hListen);
  if (pListen == NULL)
  {
    status = CC_GKI_LISTEN_NOT_FOUND;
  }
  else
  {
    if (pListen->pAliasNames)
    {
      FreeAliasNames(pListen->pAliasNames);
    }
    Free(pListen);
    if (pListenList)
    {
      status = GkiRegister();
    }
    else
    {
      status = GkiUnregister();
    }
  }

  LeaveCriticalSection(&GkiLock);
  GKDBG((1, StateName("GkiCloseListen -> State = %s", uGkiState), 0));
  return status;
} // GkiCloseListen()



HRESULT  GkiOpenListen  (DWORD hListen, PCC_ALIASNAMES pAliasNames, DWORD dwAddr, WORD wPort)
{
  register PLISTEN      pListen;
  register HRESULT      status = NOERROR;

  GKDBG((1, StateName("GkiOpenListen <- State = %s", uGkiState), 0));
  EnterCriticalSection(&GkiLock);

  // Check for invalid IP address
  if (dwAddr == INADDR_ANY || dwAddr == INADDR_NONE)
  {
    dwAddr = GetIpAddress();
    if (dwAddr == INADDR_ANY)
      return CC_GKI_IP_ADDRESS;
  }

  // Check for invalid alias list
  if (pAliasNames)
  {
    PCC_ALIASITEM       pAliasItem;
    unsigned int        uIndex;

    if (pAliasNames->wCount == 0)
    {
      GKDBG((1, "GkiOpenListen: Alias name wCount == 0", 0));
      return CC_BAD_PARAM;
    }
    pAliasItem = pAliasNames->pItems;
    for (uIndex = 0; uIndex < pAliasNames->wCount; ++uIndex, ++pAliasItem)
    {
      if (pAliasItem->wDataLength == 0 || pAliasItem->pData == NULL)
      {
        // Bad alias item
        GKDBG((1, "GkiOpenListen: Bad alias item (wDataLength = %d)",
                pAliasItem->wDataLength));
        return CC_BAD_PARAM;
      }
    }
  }

  pListen = (PLISTEN)Malloc(sizeof(*pListen));
  if (pListen)
  {
    if (pAliasNames)
    {
      status = CopyAliasNames(&pListen->pAliasNames, pAliasNames);
      if (status != NOERROR)
      {
        GKDBG((1, "GkiOpenListen: CopyAliasNames returned 0x%x", status));
        LeaveCriticalSection(&GkiLock);
        return status;
      }
    }
    else
    {
      pListen->pAliasNames = NULL;
    }

    pListen->hListen = hListen;
    pListen->dwAddr  = ntohl(dwAddr);
    pListen->wPort   = wPort;
    ListenEnqueue(pListen);
    status = GkiRegister();
  } // if
  else
  {
    GKDBG((1, "GkiOpenListen: Could not allocate listen structure", 0));
    status = CC_NO_MEMORY;
  } // else

  LeaveCriticalSection(&GkiLock);
  GKDBG((1, StateName("GkiOpenListen -> State = %s", uGkiState), 0));
  return status;
} // GkiOpenListen()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

HRESULT GkiCloseCall(PGKICALL pGkiCall)
{
  HRESULT               status = NOERROR;

  ASSERT(pGkiCall != NULL);
  GKDBG((1, CallStateName("GkiCloseCall <- Call State = %s", pGkiCall->uGkiCallState), 0));

  while (pGkiCall->pBwReqHead)
  {
    Free(BwReqDequeue(pGkiCall));
  }

  if (pGkiCall->uGkiCallState == GCS_START)
  {
    GKDBG((1, CallStateName("GkiCloseCall: Call already in state %s", pGkiCall->uGkiCallState), 0));
    status = CC_GKI_CALL_STATE;
  }
  else
  {
    switch (uGkiState)
    {
    case STATE_START:
      break;

    case STATE_REG_BYPASS:
      status = GkiFreeCall(pGkiCall);
      break;

    default:
      switch (pGkiCall->uGkiCallState)
      {
      case GCS_WAITING:
        status = GkiFreeCall(pGkiCall);
        break;

      case GCS_ADMITTING:
        pGkiCall->uGkiCallState = GCS_ADMITTING_CLOSE_PENDING;
        break;

      case GCS_ADMITTING_CLOSE_PENDING:
      case GCS_CHANGING_CLOSE_PENDING:
      case GCS_DISENGAGING:
        GKDBG((1, CallStateName("GkiCloseCall: Call already in closing state %s", pGkiCall->uGkiCallState), 0));
        status = CC_GKI_CALL_STATE;
        break;

      case GCS_ADMITTED:
        pGkiCall->uGkiCallState = GCS_DISENGAGING;
        GKDBG((1, "GKI_DisengageRequest called...", 0));
        ++uPendingDisengages;
        status = pGKI_DisengageRequest(pGkiCall->hGkiCall);
        if (status == NOERROR)
        {
          GKDBG((1, GkiErrorName("GKI_DisengageRequest returned %s", status), 0));
        }
        else
        {
          --uPendingDisengages;
          GKDBG((1, GkiErrorName("GKI_DisengageRequest returned %s", status), 0));
          GkiFreeCall(pGkiCall);
        }
        break;

      case GCS_CHANGING:
        pGkiCall->uGkiCallState = GCS_CHANGING_CLOSE_PENDING;
        break;

      default:
        GKDBG((1, CallStateName("GkiCloseCall: Call in invalid state %s", pGkiCall->uGkiCallState), 0));
        status = CC_GKI_CALL_STATE;
      } // switch
    } // switch
  } // else

  GKDBG((1, StateName("GkiCloseCall -> State = %s", uGkiState), 0));
  return status;
} // GkiCloseCall()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static HRESULT BandwidthRejected(PGKICALL pGkiCall, UINT Reason)
{
  HRESULT               status = NOERROR;
  PBWREQ                pBwReq;
  DWORD                 hCall;

  ASSERT(pGkiCall != NULL);
  pBwReq = BwReqDequeue(pGkiCall);
  hCall  = pGkiCall->hCall;

  if (pBwReq)
  {
    if ((pGkiCall->uBandwidthUsed + pBwReq->uChannelBandwidth) <= pGkiCall->uBandwidthAllocated)
    {
      if (pBwReq->Type == TX)
      {
        OpenChannelConfirm  (pBwReq->hChannel);
      }
      else
      {
        AcceptChannelConfirm(pBwReq->hChannel);
      }
    }
    else
    {
      if (pBwReq->Type == TX)
      {
        OpenChannelReject   (pBwReq->hChannel, MapBandwidthRejectReason(Reason));
      }
      else
      {
        AcceptChannelReject (pBwReq->hChannel, MapBandwidthRejectReason(Reason));
      }
    }
    Free(pBwReq);
    if (ValidateCall(hCall) == NOERROR)
    {
      CheckPendingBandwidth(pGkiCall);
    }
  }

  return status;
} // BandwidthRejected()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static HRESULT CheckPendingBandwidth(PGKICALL pGkiCall)
{
  HRESULT               status = NOERROR;
  PBWREQ                pBwReq;
  DWORD                 hCall;

  ASSERT(pGkiCall != NULL);
  ASSERT(pGkiCall->uGkiCallState == GCS_ADMITTED);
  hCall = pGkiCall->hCall;

  while (pGkiCall->pBwReqHead != NULL &&
         (pGkiCall->uBandwidthUsed + pGkiCall->pBwReqHead->uChannelBandwidth) <= pGkiCall->uBandwidthAllocated)
  {
    pBwReq = BwReqDequeue(pGkiCall);
    ASSERT(pBwReq != NULL);
    pGkiCall->uBandwidthUsed += pBwReq->uChannelBandwidth;
    if (pBwReq->Type == TX)
    {
      OpenChannelConfirm(pBwReq->hChannel);
    }
    else
    {
      AcceptChannelConfirm(pBwReq->hChannel);
    }
    Free(pBwReq);
    if (ValidateCall(hCall) != NOERROR)
    {
      return status;
    }
  }

  if (pGkiCall->pBwReqHead != NULL)
  {
    pGkiCall->uGkiCallState = GCS_CHANGING;
    GKDBG((1, "GKI_BandwidthRequest called...", 0);
    status = pGKI_BandwidthRequest(pGkiCall->hGkiCall,
                                   pGkiCall->usCallTypeChoice,
                                   pGkiCall->uBandwidthUsed + pGkiCall->pBwReqHead->uChannelBandwidth));
    if (status == NOERROR)
    {
      GKDBG((1, GkiErrorName("GKI_BandwidthRequest returned %s", status), 0));
    }
    else
    {
      GKDBG((1, GkiErrorName("GKI_BandwidthRequest returned %s", status), 0));
      BandwidthRejected(pGkiCall, BndRjctRsn_undfndRsn_chosen);
    }
  }

  return status;
} // CheckPendingBandwidth()



static void FreeAliasList(SeqAliasAddr *pAliasAddrs)
{
  register SeqAliasAddr *pAlias = pAliasAddrs;
  while (pAlias)
  {
    if (pAlias->value.choice == h323_ID_chosen && pAlias->value.u.h323_ID.value)
      Free(pAlias->value.u.h323_ID.value);
    pAlias = pAlias->next;
  }
  Free(pAlias);
} // FreeAliasList()



/*
 *  NOTES
 *    Must have Call locked before calling!
 *
 *    The following fields in the GKICALL structure must be properly filled
 *    in before calling this function:
 *      pCall                 Pointer back to containing CALL structure.
 *      CallType              Type of call.
 *      uBandwidthRequested   Initial bandwidth for call.
 *      pConferenceId         Pointer to conference ID buffer.
 *      bActiveMC             TRUE if calling party has an active MC.
 *      bAnswerCall           ???
 */

HRESULT GkiOpenCall (PGKICALL pGkiCall, void *pConference)
{
  HRESULT               status = NOERROR;
  DWORD                 hCall;
  TransportAddress      SrcCallSignalAddress;
  TransportAddress *    pSrcCallSignalAddress;
  TransportAddress      DestCallSignalAddress;
  TransportAddress *    pDestCallSignalAddress;
  SeqAliasAddr *        pAliasAddrs;
  SeqAliasAddr *        pExtraAliasAddrs;
  SeqAliasAddr *        pAlias;
  PCC_ALIASITEM         pAliasItem;
  unsigned              uCount;
  unsigned              uIndex;
  ConferenceIdentifier  ConferenceId;

  ASSERT(pGkiCall != NULL);
  ASSERT(pConference != NULL);
  GKDBG((1, StateName("GkiOpenCall <- State = %s", uGkiState), 0));
  EnterCriticalSection(&GkiLock);

  switch (uGkiState)
  {
  case STATE_REG_BYPASS:
    ASSERT(pGkiCall->uGkiCallState == GCS_START || pGkiCall->uGkiCallState == GCS_WAITING || pGkiCall->uGkiCallState == GCS_ADMITTING);
    hCall = pGkiCall->hCall;
    GkiAllocCall(pGkiCall, GKI_BYPASS_HANDLE);
    pGkiCall->uBandwidthAllocated = MAX_BANDWIDTH;
    if (pGkiCall->bAnswerCall)
    {
      status = AcceptCallConfirm(pGkiCall->pCall, pConference);
      if (status == NOERROR && ValidateCall(hCall) == NOERROR)
      {
        CheckPendingBandwidth(pGkiCall);
      }
    }
    else if (pGkiCall->dwIpAddress == 0)
    {
      status = PlaceCallReject  (pGkiCall->pCall, pConference, CC_INVALID_WITHOUT_GATEKEEPER);
    }
    else
    {
      status = PlaceCallConfirm (pGkiCall->pCall, pConference);
      if (status == NOERROR && ValidateCall(hCall) == NOERROR)
      {
        CheckPendingBandwidth(pGkiCall);
      }
    }
    break;

  case STATE_REGISTERING:
  case STATE_REGISTERING_REREG:
  case STATE_ADMITTING:
    pGkiCall->uGkiCallState = GCS_WAITING;
    break;

  case STATE_REGISTERED:
    switch (pGkiCall->CallType)
    {
    case POINT_TO_POINT:
      pGkiCall->usCallTypeChoice = pointToPoint_chosen;
      break;
    case ONE_TO_MANY:
      pGkiCall->usCallTypeChoice = oneToN_chosen;
      break;
    case MANY_TO_ONE:
      pGkiCall->usCallTypeChoice = nToOne_chosen;
      break;
    case MANY_TO_MANY:
      pGkiCall->usCallTypeChoice = nToN_chosen;
      break;
    default:
      LeaveCriticalSection(&GkiLock);
      GKDBG((1, "GkiOpenCall -> Invalid CallType %d", pGkiCall->CallType));
      return CC_BAD_PARAM;
    } // switch

    pSrcCallSignalAddress = NULL;
    pDestCallSignalAddress = NULL;
    pAliasAddrs            = NULL;
    pExtraAliasAddrs       = NULL;

    if (pGkiCall->dwSrcCallSignalIpAddress != 0 && pGkiCall->wSrcCallSignalPort != 0)
    {
      SrcCallSignalAddress.choice = ipAddress_chosen;
      SrcCallSignalAddress.u.ipAddress.ip.length = 4;
      *((DWORD *)SrcCallSignalAddress.u.ipAddress.ip.value) = pGkiCall->dwSrcCallSignalIpAddress;
      SrcCallSignalAddress.u.ipAddress.port = pGkiCall->wSrcCallSignalPort;
      pSrcCallSignalAddress = &SrcCallSignalAddress;
    }

    if (pGkiCall->dwIpAddress != 0 && pGkiCall->wPort != 0)
    {
      DestCallSignalAddress.choice = ipAddress_chosen;
      DestCallSignalAddress.u.ipAddress.ip.length = 4;
      *((DWORD *)DestCallSignalAddress.u.ipAddress.ip.value) = pGkiCall->dwIpAddress;
      DestCallSignalAddress.u.ipAddress.port = pGkiCall->wPort;
      pDestCallSignalAddress = &DestCallSignalAddress;
    }

    if (pGkiCall->pCalleeAliasNames)
    {
//      pDestCallSignalAddress = NULL;
      uCount = pGkiCall->pCalleeAliasNames->wCount;
      pAliasAddrs = Malloc(uCount * sizeof(*pAliasAddrs));
      if (pAliasAddrs == NULL)
      {
        LeaveCriticalSection(&GkiLock);
        GKDBG((1, "GkiOpenCall: Could not allocate %d Alias Addresses", uCount));
        return CC_NO_MEMORY;
      }
      memset(pAliasAddrs, 0, uCount * sizeof(*pAliasAddrs));
      pAlias = pAliasAddrs;
      pAliasItem = pGkiCall->pCalleeAliasNames->pItems;
      for (uIndex = 0; uIndex < uCount; ++uIndex)
      {
        status = CopyAliasItem(pAlias, pAliasItem);
        if (status != NOERROR)
        {
          LeaveCriticalSection(&GkiLock);
          GKDBG((1, "GkiOpenCall: CopyAliasItem returned %d", status));
          FreeAliasList(pAliasAddrs);
          return status;
        }
        pAlias->next = pAlias + 1;
        ++pAlias;
        ++pAliasItem;
      } // for
      --pAlias;
      pAlias->next = NULL;
    }

    if (pGkiCall->pCalleeExtraAliasNames)
    {
      uCount = pGkiCall->pCalleeExtraAliasNames->wCount;
      pExtraAliasAddrs = Malloc(uCount * sizeof(*pExtraAliasAddrs));
      if (pExtraAliasAddrs == NULL)
      {
        LeaveCriticalSection(&GkiLock);
        GKDBG((1, "GkiOpenCall: Could not allocate %d Alias Addresses", uCount));
        if (pAliasAddrs)
          FreeAliasList(pAliasAddrs);
        return CC_NO_MEMORY;
      }
      memset(pExtraAliasAddrs, 0, uCount * sizeof(*pExtraAliasAddrs));
      pAlias = pExtraAliasAddrs;
      pAliasItem = pGkiCall->pCalleeExtraAliasNames->pItems;
      for (uIndex = 0; uIndex < uCount; ++uIndex)
      {
        status = CopyAliasItem(pAlias, pAliasItem);
        if (status != NOERROR)
        {
          LeaveCriticalSection(&GkiLock);
          GKDBG((1, "GkiOpenCall: CopyAliasItem returned %d", status));
          if (pAliasAddrs)
            FreeAliasList(pAliasAddrs);
          FreeAliasList(pExtraAliasAddrs);
          return status;
        }
        pAlias->next = pAlias + 1;
        ++pAlias;
        ++pAliasItem;
      } // for
      --pAlias;
      pAlias->next = NULL;
    }

    if (pGkiCall->uBandwidthRequested < MIN_BANDWIDTH)
    {
      pGkiCall->uBandwidthRequested = MIN_BANDWIDTH;
    }
    ASSERT(pGkiCall->uBandwidthAllocated == 0);
    ASSERT(pGkiCall->uBandwidthUsed == 0);

    memcpy(ConferenceId.value, pGkiCall->pConferenceId, 16);
    if (((DWORD *)pGkiCall->pConferenceId)[0] != 0 ||
        ((DWORD *)pGkiCall->pConferenceId)[1] != 0 ||
        ((DWORD *)pGkiCall->pConferenceId)[2] != 0 ||
        ((DWORD *)pGkiCall->pConferenceId)[3] != 0)
    {
      ConferenceId.length = 16;
    }
    else
    {
      ConferenceId.length = 0;
    }

    pGkiCall->hGkiCall = GKI_ADMITTING_HANDLE;
    if (pDestCallSignalAddress != NULL || pAliasAddrs != NULL)
    {
      uGkiState = STATE_ADMITTING;
      pGkiCall->uGkiCallState = GCS_ADMITTING;
      GKDBG((1, "GKI_AdmissionRequest called...", 0);
      status = pGKI_AdmissionRequest(pGkiCall->usCallTypeChoice,    // usCallTypeChoice.
                                     pAliasAddrs,                   // pDestinationInfo
									 pGkiCall->bAnswerCall ? pSrcCallSignalAddress : pDestCallSignalAddress,
                                     pExtraAliasAddrs,              // pDestExtraCallInfo,
                                     pGkiCall->uBandwidthRequested, // bandWidth,
                                     &ConferenceId,                 // pConferenceID,
                                     pGkiCall->bActiveMC,           // activeMC,
                                     pGkiCall->bAnswerCall,         // answerCall,
                                     ipAddress_chosen));             // usCallTransport
      if (status == NOERROR)
      {
        GKDBG((1, GkiErrorName("GKI_AdmissionRequest returned %s", status), 0));
      }
      else
      {
        GKDBG((1, GkiErrorName("GKI_AdmissionRequest returned %s", status), 0));
      }
    }
    else
    {
      pGkiCall->hGkiCall = 0;
      status = CC_BAD_PARAM;
    }

    if (status != NOERROR)
    {
      uGkiState = STATE_REGISTERED;
      GkiCancelCall(pGkiCall, pConference);
    }

    if (pAliasAddrs)
      FreeAliasList(pAliasAddrs);
    if (pExtraAliasAddrs)
      FreeAliasList(pExtraAliasAddrs);
    break;

  case STATE_START:
  case STATE_CLASS_REGISTERED:
  case STATE_WINDOW_CREATED:
    pGkiCall->uGkiCallState = GCS_WAITING;
    status = GkiOpenListen(DEFAULT_LISTEN_HANDLE, NULL, 0, 1720);
    break;

  default:
    GKDBG((1, StateName("GkiOpenCall: Invalid state %s", uGkiState), 0));
    status = LastGkiError;
  } // switch

  LeaveCriticalSection(&GkiLock);
  GKDBG((1, CallStateName("GkiOpenCall -> Call State = %s", pGkiCall->uGkiCallState), 0));
  return status;
} // GkiOpenCall()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

HRESULT GkiOpenChannel(PGKICALL pGkiCall, unsigned uChannelBandwidth, DWORD hChannel, CHANNELTYPE Type)
{
  HRESULT               status = NOERROR;
  PBWREQ                pBwReq;

  ASSERT(pGkiCall != NULL);
  GKDBG((1, CallStateName("GkiOpenChannel <- Call State = %s", pGkiCall->uGkiCallState), 0));

  pBwReq = (PBWREQ)Malloc(sizeof(*pBwReq));
  if (pBwReq == NULL)
  {
    GKDBG((1, "GkiOpenChannel: Memory allocation failed", 0));
    return CC_NO_MEMORY;
  }

  pBwReq->uChannelBandwidth = uChannelBandwidth / 100;
  pBwReq->hChannel          = hChannel;
  pBwReq->Type              = Type;
  BwReqEnqueue(pGkiCall, pBwReq);
  switch (pGkiCall->uGkiCallState)
  {
  case GCS_WAITING:
  case GCS_ADMITTING:
  case GCS_CHANGING:
    // Must wait for current operation to complete
    break;

  case GCS_ADMITTED:
    status = CheckPendingBandwidth(pGkiCall);
    break;

  default:
    GKDBG((1, "GkiOpenChannel: Invalid call state %d", pGkiCall->uGkiCallState));
    status = CC_GKI_CALL_STATE;
  } // switch

  GKDBG((1, CallStateName("GkiOpenChannel -> Call State = %s", pGkiCall->uGkiCallState), 0));
  return status;
} // GkiOpenChannel()



/*
 *  NOTES
 *    Must have Call locked before calling!
 */

HRESULT GkiCloseChannel(PGKICALL pGkiCall, unsigned uChannelBandwidth, DWORD hChannel)
{
  PBWREQ                pBwReq;
  PBWREQ                pBwReq1;

  ASSERT(pGkiCall != NULL);
  GKDBG((1, CallStateName("GkiCloseChannel <- Call State = %s", pGkiCall->uGkiCallState), 0));

  // If Bandwidth request is still in queue, bandwidth has not been allocated
  pBwReq = pGkiCall->pBwReqHead;
  if (pBwReq)
  {
    if (pBwReq->hChannel == hChannel)
    {
      Free(BwReqDequeue(pGkiCall));
      GKDBG((1, CallStateName("GkiCloseChannel -> Call State = %s", pGkiCall->uGkiCallState), 0));
      return NOERROR;
    }
    while ((pBwReq1 = pBwReq->pNext) != NULL)
    {
      if (pBwReq1->hChannel == hChannel)
      {
        if (pGkiCall->pBwReqTail == pBwReq1)
        {
          pGkiCall->pBwReqTail = pBwReq;
        }
        pBwReq->pNext = pBwReq1->pNext;
        Free(pBwReq1);
        GKDBG((1, CallStateName("GkiCloseChannel -> Call State = %s", pGkiCall->uGkiCallState), 0));
        return NOERROR;
      }
    }
  }

  pGkiCall->uBandwidthUsed -= (uChannelBandwidth / 100);
  GKDBG((1, CallStateName("GkiCloseChannel -> Call State = %s", pGkiCall->uGkiCallState), 0));
  return NOERROR;
} // GkiCloseChannel()



unsigned GkiGetBandwidth(PGKICALL pGkiCall)
{
  ASSERT(pGkiCall != NULL);
  return pGkiCall->uBandwidthAllocated * 100;
} // GkiGetBandwidth()



//
// GkiWndProc subroutines
//

/*
 *  NOTES
 *    Must have Call locked before calling!
 */

static HRESULT CheckPendingOpen(PGKICALL pGkiCall, void *pConference)
{
  HRESULT               status = NOERROR;

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

  switch (uGkiState)
  {
  case STATE_REGISTERED:
  case STATE_REG_BYPASS:
    // TBD - Can only open 1!!!
    ASSERT(pGkiCall->uGkiCallState != GCS_ADMITTING);
    if (pGkiCall->uGkiCallState == GCS_WAITING)
    {
      status = GkiOpenCall(pGkiCall, pConference);
    }
    break;

  default:
    status = LastGkiError;
  } // switch

  return status;
} // CheckPendingOpen()



static void GkiNoResponse(HWND hWnd)
{
  HRESULT               status;

  switch (uGkiState)
  {
  case STATE_START:
  case STATE_CLASS_REGISTERED:
  case STATE_WINDOW_CREATED:
  case STATE_REG_BYPASS:
    break;

  case STATE_REGISTERING:
  case STATE_REGISTERING_REREG:
  case STATE_REGISTERING_UNREG:
    GKDBG((1, "GkiWndProc: dummy GKI_REG_REJECT", 0));
    PostMessage(hWnd, GKIMAN_BASE + GKI_REG_REJECT, 0, 0);
    break;

  case STATE_ADMITTING:
  case STATE_ADMITTING_REREG:
    ApplyToAllCalls(GkiCancelAdmitting);
    uGkiState = STATE_REGISTERED;

    // Fall-through to next case

  case STATE_REGISTERED:
    if (uGkiCalls == 0)
    {
      GkiRegister();
    }
    else
    {
      uGkiState = STATE_REG_BYPASS;
      ApplyToAllCalls(GatekeeperNotFound);
      GKDBG((1, "GKI_UnregistrationRequest called...", 0));
      status = pGKI_UnregistrationRequest();
      if (status == NOERROR)
      {
        GKDBG((1, GkiErrorName("GKI_UnregistrationRequest returned %s", status), 0));
      }
      else
      {
        GKDBG((1, GkiErrorName("GKI_UnregistrationRequest returned %s", status), 0));
      }
    }
    break;

  case STATE_ADMITTING_UNREG:
    ApplyToAllCalls(GkiCancelAdmitting);
    uGkiState = STATE_REGISTERED;
    GkiUnregister();
    break;

  case STATE_DISENGAGING:
    ApplyToAllCalls(GatekeeperNotFound);
    ASSERT(uGkiCalls == 0);
    uGkiState = STATE_REGISTERED;
    GkiUnregister();
    break;

  case STATE_DISENGAGING_REREG:
    ApplyToAllCalls(GatekeeperNotFound);
    ASSERT(uGkiCalls == 0);
    uGkiState = STATE_REGISTERED;
    GkiRegister();
    break;

  case STATE_UNREGISTERING:
  case STATE_UNREGISTERING_REREG:
    GKDBG((1, "GkiWndProc: dummy GKI_UNREG_CONFIRM", 0));
    PostMessage(hWnd, GKIMAN_BASE + GKI_UNREG_CONFIRM, 0, 0);
    break;

  default:
    GKDBG((1, "GkiWndProc: Bad uGkiState %d", uGkiState));
  } // switch
} // GkiNoResponse()



LONG APIENTRY GkiWndProc(
  HWND hWnd,                /* window handle                   */
  UINT message,             /* type of message                 */
  UINT wParam,              /* additional information          */
  LONG lParam)              /* additional information          */
{
  CallReturnInfo *      pCallReturnInfo;
  PGKICALL              pGkiCall;
  void *                pConference;
  DWORD                 hCall;
  DWORD                 hConference;
  HRESULT               status;

  if (message < GKIMAN_BASE)
  {
    return DefWindowProc(hWnd, message, wParam, lParam);
  }

  EnterCallControlTop();

  GKDBG((1, StateName("GkiWndProc <- State = %s", uGkiState), 0));

  switch (message)
  {
  case GKIMAN_BASE + GKI_REG_CONFIRM:
    GKDBG((1, "GkiWndProc: GKI_REG_CONFIRM", 0));
    switch (uGkiState)
    {
    case STATE_REGISTERING:
      uGkiState = STATE_REGISTERED;
      ApplyToAllCalls(CheckPendingOpen);
      break;
    case STATE_REGISTERING_REREG:
      uGkiState = STATE_REGISTERED;
      GkiRegister();
      break;
    case STATE_REGISTERING_UNREG:
      uGkiState = STATE_REGISTERED;
      GkiUnregister();
      break;
    default:
      GKDBG((1, StateName("GkiWndProc: GKI_REG_CONFIRM in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_REG_DISCOVERY:
    GKDBG((1, "GkiWndProc: GKI_REG_DISCOVERY", 0));
    ASSERT(uGkiState == STATE_REGISTERING || uGkiState == STATE_REGISTERING_REREG || uGkiState == STATE_REGISTERING_UNREG);
    break;

  case GKIMAN_BASE + GKI_REG_REJECT:
    GKDBG((1, "GkiWndProc: GKI_REG_REJECT Reason = %d", wParam));
    switch (uGkiState)
    {
    case STATE_REGISTERING:
      ApplyToAllCalls(GkiCancelCall);
      EnterCriticalSection(&GkiLock);
      while (pListenList)
      {
        register PLISTEN pListen = pListenList;
        pListenList = pListen->pNext;
        LeaveCriticalSection(&GkiLock);
        ListenReject(pListen->hListen, MapRegistrationRejectReason(wParam));
        if (pListen->pAliasNames)
        {
            FreeAliasNames(pListen->pAliasNames);
        }
        Free(pListen);
        EnterCriticalSection(&GkiLock);
      }
      LeaveCriticalSection(&GkiLock);
      uGkiState = STATE_WINDOW_CREATED;
      break;
    case STATE_REGISTERING_REREG:
      uGkiState = STATE_WINDOW_CREATED;
      GkiRegister();
      break;
    case STATE_REGISTERING_UNREG:
      uGkiState = STATE_WINDOW_CREATED;
      GkiUnregister();
      break;
    default:
      GKDBG((1, StateName("GkiWndProc: GKI_REG_REJECT in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_REG_BYPASS:
    GKDBG((1, "GkiWndProc: GKI_REG_BYPASS", 0));
    switch (uGkiState)
    {
    case STATE_REGISTERING:
    case STATE_REGISTERING_REREG:
      uGkiState = STATE_REG_BYPASS;
      ApplyToAllCalls(CheckPendingOpen);
      break;
    case STATE_REGISTERING_UNREG:
      uGkiState = STATE_WINDOW_CREATED;
      GkiUnregister();
      break;
    default:
      GKDBG((1, StateName("GkiWndProc: GKI_REG_BYPASS in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_UNREG_CONFIRM:
    GKDBG((1, "GkiWndProc: GKI_UNREG_CONFIRM", 0));
    switch (uGkiState)
    {
    case STATE_REGISTERING:
    case STATE_REGISTERING_REREG:
    case STATE_REGISTERED:
    case STATE_ADMITTING:
    case STATE_ADMITTING_REREG:
    case STATE_DISENGAGING_REREG:
    case STATE_UNREGISTERING_REREG:
      uGkiState = STATE_WINDOW_CREATED;
      GkiRegister();
      break;

    default:
      GKDBG((1, StateName("GkiWndProc: GKI_UNREG_CONFIRM in state %s", uGkiState), 0));

      // Fall through to next case

    case STATE_ADMITTING_UNREG:
    case STATE_DISENGAGING:
      ApplyToAllCalls(GkiCancelCall);

      // Fall-through to next case

    case STATE_REGISTERING_UNREG:
    case STATE_UNREGISTERING:
      uGkiState = STATE_WINDOW_CREATED;

      // Fall-through to next case

    case STATE_CLASS_REGISTERED:
    case STATE_WINDOW_CREATED:

      // Fall-through to next case

    case STATE_START:
    case STATE_REG_BYPASS:
      break;

    } // switch
    break;

  case GKIMAN_BASE + GKI_UNREG_REJECT:
    GKDBG((1, "GkiWndProc: GKI_UNREG_REJECT Reason = %d", wParam));
    switch (uGkiState)
    {
    case STATE_UNREGISTERING:
      uGkiState = STATE_WINDOW_CREATED;
      GkiUnregister();
      break;
    case STATE_UNREGISTERING_REREG:
      uGkiState = STATE_WINDOW_CREATED;
      GkiRegister();
      break;
    default:
      GKDBG((1, StateName("GkiWndProc: GKI_UNREG_REJECT in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_ADM_CONFIRM:
    GKDBG((1, "GkiWndProc: GKI_ADM_CONFIRM", 0));
    switch (uGkiState)
    {
    case STATE_ADMITTING:
      uGkiState = STATE_REGISTERED;
      pCallReturnInfo = (CallReturnInfo *) lParam;
      if (LockGkiCallAndConference(GKI_ADMITTING_HANDLE, &pGkiCall, &pConference, &hCall, &hConference) == NOERROR)
      {
        GKDBG((1, CallStateName("GkiWndProc: Call State = %s", pGkiCall->uGkiCallState), 0));
        pGkiCall->usCallModelChoice   = pCallReturnInfo->callModel.choice;
        pGkiCall->uBandwidthAllocated = pCallReturnInfo->bandWidth;
        pGkiCall->usCRV = pCallReturnInfo->callReferenceValue;
        memcpy(pGkiCall->pConferenceId, pCallReturnInfo->conferenceID.value, 16);
        switch (pGkiCall->uGkiCallState)
        {
        case GCS_ADMITTING:
          GkiAllocCall(pGkiCall, pCallReturnInfo->hCall);
          if (pGkiCall->bAnswerCall)
          {
            status = AcceptCallConfirm(pGkiCall->pCall, pConference);
          }
          else
          {
            ASSERT(pCallReturnInfo->destCallSignalAddress.choice == ipAddress_chosen);
            pGkiCall->dwIpAddress = *((DWORD *)pCallReturnInfo->destCallSignalAddress.u.ipAddress.ip.value);
            pGkiCall->dwIpAddress = pGkiCall->dwIpAddress;
            pGkiCall->wPort = pCallReturnInfo->destCallSignalAddress.u.ipAddress.port;
            status = PlaceCallConfirm(pGkiCall->pCall, pConference);
          }
          if (status == NOERROR && ValidateCall(hCall) == NOERROR)
            CheckPendingBandwidth(pGkiCall);
          break;

        case GCS_ADMITTING_CLOSE_PENDING:
          GkiAllocCall(pGkiCall, pCallReturnInfo->hCall);
          GkiCloseCall(pGkiCall);
          break;

        default:
          GKDBG((1, CallStateName("GkiWndProc: GKI_ADM_CONFIRM in call state %s", pGkiCall->uGkiCallState), 0));
        } // switch
        UnlockGkiCallAndConference(pGkiCall, pConference, hCall, hConference);
      } // if
      else
      {
        GKDBG((1, "GkiWndProc: GKI_ADM_CONFIRM handle not found", 0));
      }
      ApplyToAllCalls(CheckPendingOpen);
      break;

    case STATE_ADMITTING_UNREG:
      uGkiState = STATE_REGISTERED;
      GkiUnregister();
      break;

    default:
        GKDBG((1, StateName("GkiWndProc: GKI_ADM_CONFIRM in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_ADM_REJECT:
    GKDBG((1, "GkiWndProc: GKI_ADM_REJECT Reason = %d", wParam));
    switch (uGkiState)
    {
    case STATE_ADMITTING:
      pCallReturnInfo = (CallReturnInfo *) lParam;
      if (LockGkiCallAndConference(GKI_ADMITTING_HANDLE, &pGkiCall, &pConference, &hCall, &hConference) == NOERROR)
      {
		ASSERT(pGkiCall->uGkiCallState == GCS_ADMITTING);
        switch (wParam)
        {
        case calledPartyNotRegistered_chosen:
          if (pGkiCall->bAnswerCall)
          {
            // The gateway has gone away and come back without our notice!
            GkiCancelAdmitting(pGkiCall, pConference);
            uGkiState = STATE_REGISTERED;
            GkiRegister();
            GKDBG((1, StateName("GkiWndProc -> State = %s", uGkiState), 0));
            LeaveCallControlTop(0);
          }
          break;
        case callerNotRegistered_chosen:
          if (pGkiCall->bAnswerCall == FALSE)
          {
            // The gateway has gone away and come back without our notice!
            GkiCancelAdmitting(pGkiCall, pConference);
            uGkiState = STATE_REGISTERED;
            GkiRegister();
            GKDBG((1, StateName("GkiWndProc -> State = %s", uGkiState), 0));
            LeaveCallControlTop(0);
          }
        } // switch
        GkiFreeCall(pGkiCall);
        if (pGkiCall->bAnswerCall)
          AcceptCallReject(pGkiCall->pCall, pConference, MapAdmissionRejectReason(wParam));
        else
          PlaceCallReject (pGkiCall->pCall, pConference, MapAdmissionRejectReason(wParam));
        UnlockGkiCallAndConference(pGkiCall, pConference, hCall, hConference);
      } // if
      else
      {
        GKDBG((1, "GkiWndProc: GKI_ADM_REJECT handle not found", 0));
      }
      uGkiState = STATE_REGISTERED;
      ApplyToAllCalls(CheckPendingOpen);
      break;

    case STATE_ADMITTING_REREG:
      pCallReturnInfo = (CallReturnInfo *) lParam;
      if (LockGkiCallAndConference(GKI_ADMITTING_HANDLE, &pGkiCall, &pConference, &hCall, &hConference) == NOERROR)
      {
		ASSERT(pGkiCall->uGkiCallState == GCS_ADMITTING_CLOSE_PENDING);
        GkiFreeCall(pGkiCall);
        if (pGkiCall->bAnswerCall)
        {
          AcceptCallReject(pGkiCall->pCall, pConference, MapAdmissionRejectReason(wParam));
        }
        else
        {
          PlaceCallReject (pGkiCall->pCall, pConference, MapAdmissionRejectReason(wParam));
        }
        UnlockGkiCallAndConference(pGkiCall, pConference, hCall, hConference);
      } // if
      else
      {
        GKDBG((1, "GkiWndProc: GKI_ADM_REJECT handle not found", 0));
      }
      uGkiState = STATE_REGISTERED;
      GkiRegister();
      break;

    case STATE_ADMITTING_UNREG:
      pCallReturnInfo = (CallReturnInfo *) lParam;
      if (LockGkiCallAndConference(GKI_ADMITTING_HANDLE, &pGkiCall, &pConference, &hCall, &hConference) == NOERROR)
      {
		ASSERT(pGkiCall->uGkiCallState == GCS_ADMITTING_CLOSE_PENDING);
        GkiFreeCall(pGkiCall);
        if (pGkiCall->bAnswerCall)
        {
          AcceptCallReject(pGkiCall->pCall, pConference, MapAdmissionRejectReason(wParam));
        }
        else
        {
          PlaceCallReject (pGkiCall->pCall, pConference, MapAdmissionRejectReason(wParam));
        }
        UnlockGkiCallAndConference(pGkiCall, pConference, hCall, hConference);
      } // if
      else
      {
        GKDBG((1, "GkiWndProc: GKI_ADM_REJECT handle not found", 0));
      }
      uGkiState = STATE_REGISTERED;
      GkiUnregister();
      break;

    default:
        GKDBG((1, StateName("GkiWndProc: GKI_ADM_REJECT in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_BW_CONFIRM:
    GKDBG((1, "GkiWndProc: GKI_BW_CONFIRM", 0));
    switch (uGkiState)
    {
    case STATE_REGISTERED:
      pCallReturnInfo = (CallReturnInfo *) lParam;
      if (LockGkiCallAndConference(pCallReturnInfo->hCall, &pGkiCall, &pConference, &hCall, &hConference) == NOERROR)
      {
        GKDBG((1, CallStateName("GkiWndProc: Call State = %s", pGkiCall->uGkiCallState), 0));
        pGkiCall->uBandwidthAllocated = pCallReturnInfo->bandWidth;
        switch (pGkiCall->uGkiCallState)
        {
        case GCS_ADMITTED:
          if (pGkiCall->uBandwidthUsed < pGkiCall->uBandwidthAllocated)
          {
            BandwidthShrunk(pGkiCall->pCall,
                            pConference,
                            pGkiCall->uBandwidthAllocated,
                            ((long)pGkiCall->uBandwidthAllocated) - ((long)pGkiCall->uBandwidthUsed));
          }
          break;

        case GCS_CHANGING:
          pGkiCall->uGkiCallState = GCS_ADMITTED;
          CheckPendingBandwidth(pGkiCall);
          break;

        case GCS_CHANGING_CLOSE_PENDING:
          pGkiCall->uGkiCallState = GCS_ADMITTED;
          GkiCloseCall(pGkiCall);
          break;

        default:
          GKDBG((1, CallStateName("GkiWndProc: GKI_BW_CONFIRM in call state %s", pGkiCall->uGkiCallState), 0));
        } // switch
        UnlockGkiCallAndConference(pGkiCall, pConference, hCall, hConference);
      } // if
      else
      {
        GKDBG((1, "GkiWndProc: GKI_BW_CONFIRM handle not found", 0));
      } // else
      break;

    default:
      GKDBG((1, StateName("GkiWndProc: GKI_BW_CONFIRM in GKI state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_BW_REJECT:
    GKDBG((1, "GkiWndProc: GKI_BW_REJECT Reason = %d", wParam));
    switch (uGkiState)
    {
    case STATE_REGISTERED:
      pCallReturnInfo = (CallReturnInfo *) lParam;
      if (LockGkiCallAndConference(pCallReturnInfo->hCall, &pGkiCall, &pConference, &hCall, &hConference) == NOERROR)
      {
        GKDBG((1, CallStateName("GkiWndProc: Call State = %s", pGkiCall->uGkiCallState), 0));
        pGkiCall->uBandwidthAllocated = pCallReturnInfo->bandWidth;
        switch (pGkiCall->uGkiCallState)
        {
        case GCS_CHANGING:
          pGkiCall->uGkiCallState = GCS_ADMITTED;
          BandwidthRejected(pGkiCall, wParam);
          if (ValidateCall(hCall) == NOERROR)
          {
            CheckPendingBandwidth(pGkiCall);
          }
          break;

        case GCS_CHANGING_CLOSE_PENDING:
          pGkiCall->uGkiCallState = GCS_ADMITTED;
          GkiCloseCall(pGkiCall);
          break;

        default:
          GKDBG((1, CallStateName("GkiWndProc: GKI_BW_REJECT in state %s", pGkiCall->uGkiCallState), 0));
        } // switch
        UnlockGkiCallAndConference(pGkiCall, pConference, hCall, hConference);
      } // if
      else
      {
        GKDBG((1, "GkiWndProc: GKI_BW_REJECT handle not found", 0));
      }
      break;

    default:
      GKDBG((1, StateName("GkiWndProc: GKI_BW_REJECT in state %s", uGkiState), 0));
    } // switch
    break;

  case GKIMAN_BASE + GKI_DISENG_CONFIRM:
    GKDBG((1, "GkiWndProc: GKI_DISENG_CONFIRM", 0));
    if (LockGkiCall((HANDLE)lParam, &pGkiCall) == NOERROR)
    {
      GKDBG((1, CallStateName("GkiWndProc: Call State = %s", pGkiCall->uGkiCallState), 0));
      switch (pGkiCall->uGkiCallState)
      {
      case GCS_DISENGAGING:
        --uPendingDisengages;
        break;

      default:
        GKDBG((1, CallStateName("GkiWndProc: GKI_DISENG_CONFIRM in call state %s", pGkiCall->uGkiCallState), 0));
      } // switch
      GkiFreeCall(pGkiCall);
      Disengage(pGkiCall->pCall);
    } // if
    else if (uPendingDisengages != 0)
    {
      --uPendingDisengages;
      if (uPendingDisengages == 0)
      {
        switch (uGkiState)
        {
        case STATE_DISENGAGING:
          uGkiState = STATE_REGISTERED;
          GkiUnregister();
          break;
        case STATE_DISENGAGING_REREG:
          uGkiState = STATE_REGISTERED;
          GkiRegister();
          break;
        } // switch
        if (pListenList && pListenList->hListen == DEFAULT_LISTEN_HANDLE)
        {
          GkiCloseListen(DEFAULT_LISTEN_HANDLE);
        }
      } // if
    } // else if
    else
    {
      GKDBG((1, "GkiWndProc: GKI_DISENG_CONFIRM handle not found", 0));
    }
    break;

  case GKIMAN_BASE + GKI_DISENG_REJECT:
    GKDBG((1, "GkiWndProc: GKI_DISENG_REJECT Reason = %d", wParam));
    if (LockGkiCall((HANDLE)lParam, &pGkiCall) == NOERROR)
    {
      GKDBG((1, CallStateName("GkiWndProc: Call State = %s", pGkiCall->uGkiCallState), 0));
      switch (pGkiCall->uGkiCallState)
      {
      case GCS_DISENGAGING:
        // Pretend we received a Disengage Confirm
        --uPendingDisengages;
        break;

      default:
        GKDBG((1, CallStateName("GkiWndProc: GKI_DISENG_REJECT in call state %s", pGkiCall->uGkiCallState), 0));
      } // switch
      GkiFreeCall(pGkiCall);
      Disengage(pGkiCall->pCall);
    } // if
    else if (uPendingDisengages != 0)
    {
      // Pretend we received a Disengage Confirm
      --uPendingDisengages;
      if (uPendingDisengages == 0)
      {
        switch (uGkiState)
        {
        case STATE_DISENGAGING:
          uGkiState = STATE_REGISTERED;
          GkiUnregister();
          break;
        case STATE_DISENGAGING_REREG:
          uGkiState = STATE_REGISTERED;
          GkiRegister();
          break;
        } // switch
        if (pListenList && pListenList->hListen == DEFAULT_LISTEN_HANDLE)
        {
          GkiCloseListen(DEFAULT_LISTEN_HANDLE);
        }
      } // if
    } // else if
    else
    {
      GKDBG((1, "GkiWndProc: GKI_DISENG_REJECT handle not found", 0));
    }
    break;

  case GKIMAN_BASE + GKI_LOCATION_CONFIRM:
    GKDBG((1, "GkiWndProc: GKI_LOCATION_CONFIRM", 0));
    break;

  case GKIMAN_BASE + GKI_LOCATION_REJECT:
    GKDBG((1, "GkiWndProc: GKI_LOCATION_REJECT Reason = %d", wParam));
    break;

  case GKIMAN_BASE + GKI_ERROR:
    GKDBG((1, GkiErrorName("GkiWndProc: GKI_ERROR %s %%d", lParam), wParam));
    switch (lParam)
    {
    case GKI_NO_RESPONSE:
	  LastGkiError = lParam;
      GkiNoResponse(hWnd);
      break;
#if 1
// TEMPORARY KLUDGE FOR WINSOCK 2 BETA 1.6 OPERATION
	case MAKE_CUSTOM_HRESULT(SEVERITY_ERROR,1,FACILITY_WINSOCK,0xffff):
      uGkiState = STATE_REG_BYPASS;
      ApplyToAllCalls(CheckPendingOpen);
	  break;
#endif
    default:
	  LastGkiError = lParam;
      GkiUnregister();
    } // switch
    break;

  default:
    GKDBG((1, "Unknown message %d", message));
  } // switch

  GKDBG((1, StateName("GkiWndProc -> State = %s", uGkiState), 0));
  LeaveCallControlTop(0);
} // GkiWndProc()

#if defined(DBG)

DWORD g_dwH225DbgLevel = 0;
BOOL  g_fInitialized = FALSE;

void GKDbgInit() {

#define H323_REGKEY_ROOT \
    TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\H323TSP")

#define H323_REGVAL_DEBUGLEVEL \
    TEXT("DebugLevel")

#define H323_REGVAL_H225DEBUGLEVEL \
    TEXT("H225DebugLevel")

    HKEY hKey;
    LONG lStatus;
    DWORD dwValue;
    DWORD dwValueSize;
    DWORD dwValueType;
    LPSTR pszValue;
    LPSTR pszKey = H323_REGKEY_ROOT;

    // only call this once
    g_fInitialized = TRUE;

    // open registry subkey
    lStatus = RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                pszKey,
                0,
                KEY_READ,
                &hKey
                );

    // validate return code
    if (lStatus != ERROR_SUCCESS) {
        return; // bail...
    }
    
    // initialize values
    dwValueType = REG_DWORD;
    dwValueSize = sizeof(DWORD);

    // retrieve ras debug level
    pszValue = H323_REGVAL_H225DEBUGLEVEL;

    // query for registry value
    lStatus = RegQueryValueEx(
                hKey,
                pszValue,
                NULL,
                &dwValueType,
                (LPBYTE)&dwValue,
                &dwValueSize
                );                    

    // validate return code
    if (lStatus != ERROR_SUCCESS) {

        // initialize values
        dwValueType = REG_DWORD;
        dwValueSize = sizeof(DWORD);

        // retrieve tsp debug level
        pszValue = H323_REGVAL_DEBUGLEVEL;

        // query for registry value
        lStatus = RegQueryValueEx(
                    hKey,
                    pszValue,
                    NULL,
                    &dwValueType,
                    (LPBYTE)&dwValue,
                    &dwValueSize
                    );
    }

    // validate return code
    if (lStatus == ERROR_SUCCESS) {

        // update debug level
        g_dwH225DbgLevel = dwValue;
    }

    // close key
    RegCloseKey(hKey);
}

void GKDbgPrint(DWORD dwLevel,

#ifdef UNICODE_TRACE
				LPTSTR pszFormat,
#else
				LPSTR pszFormat,
#endif               
                ...)
{
#define DEBUG_FORMAT_HEADER     "H225 "
#define DEBUG_FORMAT_TIMESTAMP  "[%02u:%02u:%02u.%03u"
#define DEBUG_FORMAT_THREADID   ",tid=%x] "

#define MAXDBG_STRLEN        512

    va_list Args;
    SYSTEMTIME SystemTime;
    char szDebugMessage[MAXDBG_STRLEN+1];
    int nLengthRemaining;
    int nLength = 0;

    // make sure initialized
    if (g_fInitialized == FALSE) {
        GKDbgInit();
    }

    // validate debug log level
    if (dwLevel > g_dwH225DbgLevel) {
        return; // bail...
    }

    // retrieve local time
    GetLocalTime(&SystemTime);

    // add component header to the debug message
    nLength += sprintf(&szDebugMessage[nLength],
                       DEBUG_FORMAT_HEADER
                       );

    // add timestamp to the debug message
    nLength += sprintf(&szDebugMessage[nLength],
                       DEBUG_FORMAT_TIMESTAMP,
                       SystemTime.wHour,
                       SystemTime.wMinute,
                       SystemTime.wSecond,
                       SystemTime.wMilliseconds
                       );

    // add thread id to the debug message
    nLength += sprintf(&szDebugMessage[nLength],
                       DEBUG_FORMAT_THREADID,
                       GetCurrentThreadId()
                       );

    // point at first argument
    va_start(Args, pszFormat);

    // determine number of bytes left in buffer
    nLengthRemaining = sizeof(szDebugMessage) - nLength;

    // add user specified debug message
    _vsnprintf(&szDebugMessage[nLength],
               nLengthRemaining,
               pszFormat,
               Args
               );

    // release pointer
    va_end(Args);

    // output message to specified sink
    OutputDebugString(szDebugMessage);
    OutputDebugString("\n");
}

#endif

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