/*
 * Copyright (C) 2005 by Page Iberica, S.A.
 * Copyright (C) 2005 by Objective Systems, Inc.
 *
 * This software is furnished under an open source license and may be 
 * used and copied only in accordance with the terms of this license. 
 * The text of the license may generally be found in the root 
 * directory of this installation in the COPYING file.  It 
 * can also be viewed online at the following URL:
 *
 *   http://www.obj-sys.com/open/license.html
 *
 * Any redistributions of this file including modified versions must 
 * maintain this copyright notice.
 *
 *****************************************************************************/

/**
 * @file ooGkClient.c 
 * This file contains functions to support RAS protocol. 
 *
 */
#include "asterisk.h"
#include "asterisk/lock.h"

#include "ooGkClient.h"
#include "ootypes.h"
#include "ootrace.h"
#include "ooports.h"
#include "ooasn1.h"
#include "oochannels.h"
#include "printHandler.h"
#include "ooCalls.h"
#include "H323-MESSAGES.h"
#include "ooDateTime.h"
#include "ooq931.h"
#include "ooh323.h"
#include "ooh323ep.h"
#include "ooTimer.h"
#include "ooSocket.h"
#include "ooUtils.h"

/** Global endpoint structure */
extern OOH323EndPoint gH323ep;

static ASN1OBJID gProtocolID = {
   6, { 0, 0, 8, 2250, 0, 4 }
};

int ooGkClientInit(enum RasGatekeeperMode eGkMode,
              char *szGkAddr, int iGkPort )
{
   ooGkClient *pGkClient=NULL;
   OOInterface *cur=NULL;
   pGkClient = (ooGkClient*)
                         memAlloc(&gH323ep.ctxt, sizeof(ooGkClient));
   if(!pGkClient)
   {
      OOTRACEERR1("Error: Failed to allocate memory to Gatekeeper Client.\n");
      return OO_FAILED;
   }
 
   memset(pGkClient, 0, sizeof(ooGkClient));
   ast_mutex_init(&pGkClient->Lock);
   gH323ep.gkClient = pGkClient;
   initContext(&(pGkClient->ctxt));
   initContext(&(pGkClient->msgCtxt));
   pGkClient->rrqRetries = 0;
   pGkClient->grqRetries = 0;

   strcpy(pGkClient->localRASIP, gH323ep.signallingIP);
#ifndef _WIN32
   if(!strcmp(pGkClient->localRASIP, "0.0.0.0") ||
      !strcmp(pGkClient->localRASIP, "127.0.0.1"))
   {
      if(!gH323ep.ifList)
      {
         if(ooSocketGetInterfaceList(&gH323ep.ctxt, &gH323ep.ifList)!= ASN_OK)
         {
            OOTRACEERR1("Error:Failed to retrieve interface addresses\n");
            return OO_FAILED;
         }
      }
      for(cur = gH323ep.ifList; cur; cur = cur->next)
      {
         if(!strcmp(cur->name, "lo") || !strcmp(cur->addr, "127.0.0.1"))
            continue;
         break;
      }
      if(cur)
      {
         OOTRACEINFO2("Using local RAS Ip address %s\n", cur->addr);
         strcpy(pGkClient->localRASIP, cur->addr);
      }
      else{
         OOTRACEERR1("Error:Failed to assign a local RAS IP address\n");
         return OO_FAILED;
      }
   }
#endif   
   if(OO_OK != ooGkClientSetGkMode(pGkClient, eGkMode, szGkAddr, iGkPort))
   {
      OOTRACEERR1("Error:Failed to set Gk mode\n");
      memReset(&gH323ep.ctxt);
      return OO_FAILED;
   }
  
   /* Create default parameter set */
   pGkClient->grqTimeout = DEFAULT_GRQ_TIMEOUT;
   pGkClient->rrqTimeout = DEFAULT_RRQ_TIMEOUT;
   pGkClient->regTimeout = DEFAULT_REG_TTL;
   pGkClient->arqTimeout = DEFAULT_ARQ_TIMEOUT;
   pGkClient->drqTimeout = DEFAULT_DRQ_TIMEOUT;
   dListInit(&pGkClient->callsPendingList);
   dListInit(&pGkClient->callsAdmittedList);
   dListInit(&pGkClient->timerList);
   pGkClient->state = GkClientIdle;
   return OO_OK;
}


int ooGkClientSetCallbacks
   (ooGkClient *pGkClient, OOGKCLIENTCALLBACKS callbacks)
{
   pGkClient->callbacks.onReceivedRegistrationConfirm = 
                                      callbacks.onReceivedRegistrationConfirm;
   pGkClient->callbacks.onReceivedUnregistrationConfirm = 
                                     callbacks.onReceivedUnregistrationConfirm;
   pGkClient->callbacks.onReceivedUnregistrationRequest = 
                                     callbacks.onReceivedUnregistrationRequest;
   return OO_OK;
}

int ooGkClientReInit(ooGkClient *pGkClient)
{

   ooGkClientCloseChannel(pGkClient);
   pGkClient->gkRasIP[0]='\0';
   pGkClient->gkCallSignallingIP[0]='\0';
   pGkClient->gkRasPort = 0;
   pGkClient->gkCallSignallingPort = 0;
   pGkClient->rrqRetries = 0;
   pGkClient->grqRetries = 0;
   pGkClient->requestSeqNum = 0;
   
   dListFreeAll(&pGkClient->ctxt, &pGkClient->callsPendingList);
   dListFreeAll(&pGkClient->ctxt, &pGkClient->callsAdmittedList);
   dListFreeAll(&pGkClient->ctxt, &pGkClient->timerList);
   pGkClient->state = GkClientIdle;
   return OO_OK;
}

void ooGkClientPrintConfig(ooGkClient *pGkClient)
{
   OOTRACEINFO1("Gatekeeper Client Configuration:\n");
   if(pGkClient->gkMode == RasUseSpecificGatekeeper)
   {
      OOTRACEINFO1("\tGatekeeper mode - UseSpecificGatekeeper\n");
      OOTRACEINFO3("\tGatekeeper To Use - %s:%d\n", pGkClient->gkRasIP, 
                                                    pGkClient->gkRasPort);
   }
   else if(pGkClient->gkMode == RasDiscoverGatekeeper) {
      OOTRACEINFO1("\tGatekeeper mode - RasDiscoverGatekeeper\n");
   }
   else {
      OOTRACEERR1("Invalid GatekeeperMode\n");
   }
}

int ooGkClientDestroy(void)
{
   if(gH323ep.gkClient)
   {
      if(gH323ep.gkClient->state == GkClientRegistered)
      {
         OOTRACEINFO1("Unregistering from Gatekeeper\n");
         if(ooGkClientSendURQ(gH323ep.gkClient, NULL)!=OO_OK)
            OOTRACEERR1("Error:Failed to send URQ to gatekeeper\n");
      }
      OOTRACEINFO1("Destroying Gatekeeper Client\n");
      ooGkClientCloseChannel(gH323ep.gkClient);
      freeContext(&gH323ep.gkClient->msgCtxt);
      freeContext(&gH323ep.gkClient->ctxt);
      ast_mutex_lock(&gH323ep.gkClient->Lock);
      ast_mutex_unlock(&gH323ep.gkClient->Lock);
      ast_mutex_destroy(&gH323ep.gkClient->Lock);
      memFreePtr(&gH323ep.ctxt, gH323ep.gkClient);
      gH323ep.gkClient = NULL;
   }
   return OO_OK;
}

int ooGkClientStart(ooGkClient *pGkClient)
{
   int iRet=0;
   iRet = ooGkClientCreateChannel(pGkClient);

   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error: GkClient Channel Creation failed\n");
      return OO_FAILED;
   }
   
   ast_mutex_lock(&pGkClient->Lock);
   pGkClient->discoveryComplete = FALSE;
   iRet = ooGkClientSendGRQ(pGkClient);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send GRQ message\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}
   

int ooGkClientSetGkMode(ooGkClient *pGkClient, enum RasGatekeeperMode eGkMode, 
                        char *szGkAddr, int iGkPort )
{
   pGkClient->gkMode = eGkMode;
   if(eGkMode == RasUseSpecificGatekeeper)
   {
      OOTRACEINFO1("Gatekeeper Mode - RasUseSpecificGatekeeper\n");
      if(szGkAddr)
      {
         if(strlen(szGkAddr)>MAX_IP_LEN)
         { 
            OOTRACEERR2("Error:Invalid IP address specified - %s\n", szGkAddr);
            return OO_FAILED;
         }
         strcpy(pGkClient->gkRasIP, szGkAddr);
      }
      if(iGkPort)
         pGkClient->gkRasPort = iGkPort;
      else
         pGkClient->gkRasPort = DEFAULT_GKPORT;

      OOTRACEINFO3("Gatekeeper IP:port set to - %s:%d\n", 
                    szGkAddr,  pGkClient->gkRasPort);
   }
   else if(eGkMode == RasDiscoverGatekeeper) {
      OOTRACEINFO1("Gatekeeper Mode - RasDiscoverGatekeeper\n");
   }
   else if(eGkMode == RasNoGatekeeper) {
      OOTRACEINFO1("Gatekeeper Mode - RasNoGatekeeper\n");
   }

   return OO_OK;
}

   
/**
 * Create the RAS channel (socket).
 *
 */

int ooGkClientCreateChannel(ooGkClient *pGkClient)
{
   int ret=0;
   OOIPADDR ipaddrs;
   /* Create socket */
   if((ret=ooSocketCreateUDP(&pGkClient->rasSocket))!=ASN_OK)
   {
      OOTRACEERR1("Failed to create RAS socket\n");
      pGkClient->state = GkClientFailed;
      return OO_FAILED;
   }
   if(pGkClient->localRASPort)
   {
      ret= ooSocketStrToAddr (pGkClient->localRASIP, &ipaddrs);
      if( (ret=ooSocketBind( pGkClient->rasSocket, ipaddrs, 
           pGkClient->localRASPort))!=ASN_OK ) 
      {
         OOTRACEERR1("ERROR:Failed to create RAS channel\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }
   }
   else {
      ret = ooBindPort (OOUDP, pGkClient->rasSocket, pGkClient->localRASIP);
      if(ret == OO_FAILED)
      {
         OOTRACEERR1("ERROR: Failed to bind port to RAS socket\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }
      pGkClient->localRASPort = ret;
   }
   /* Test Code NOTE- This doesn't work..:(( Have to fix this */
   /* If multihomed, get ip from socket */
   if(!strcmp(pGkClient->localRASIP, "0.0.0.0"))
   {
      OOTRACEDBGA1("Determining ip address for RAS channel "
                   "multihomed mode. \n");
      ret = ooSocketGetIpAndPort(pGkClient->rasSocket, pGkClient->localRASIP, 
                                 20, &pGkClient->localRASPort);
      if(ret != ASN_OK)
      {
         OOTRACEERR1("Error:Failed to retrieve local ip and port from "
                     "socket for RAS channel(multihomed).\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }
      OOTRACEDBGA3("Using local ip %s and port %d for RAS channel"
                   "(multihomedMode).\n", pGkClient->localRASIP, 
                   pGkClient->localRASPort);
   }
   /* End of Test code */
   OOTRACEINFO1("H323 RAS channel creation - successful\n");
   return OO_OK;
}


int ooGkClientCloseChannel(ooGkClient *pGkClient)
{
   int ret;
   if(pGkClient->rasSocket != 0)
   {
      ret = ooSocketClose(pGkClient->rasSocket);
      if(ret != ASN_OK)
      {
         OOTRACEERR1("Error: failed to close RAS channel\n");
         pGkClient->rasSocket = 0;
         return OO_FAILED;
      }
      pGkClient->rasSocket = 0;
   }
   OOTRACEINFO1("Closed RAS channel\n");
   return OO_OK;
}


/**
 * Fill vendor data in RAS message structure.
 */

void ooGkClientFillVendor
   (ooGkClient *pGkClient, H225VendorIdentifier *pVendor )
{
   pVendor->vendor.t35CountryCode = gH323ep.t35CountryCode;
   pVendor->vendor.t35Extension = gH323ep.t35Extension;
   pVendor->vendor.manufacturerCode = gH323ep.manufacturerCode;
   pVendor->enterpriseNumber.numids=0;
   if(gH323ep.productID)
   {
      pVendor->m.productIdPresent = TRUE;
      pVendor->productId.numocts = ASN1MIN(strlen(gH323ep.productID), 
                                             sizeof(pVendor->productId.data));
      memcpy(pVendor->productId.data, gH323ep.productID, 
                                                  pVendor->productId.numocts);
   }
   if(gH323ep.versionID)
   {
      pVendor->m.versionIdPresent = 1;
      pVendor->versionId.numocts = ASN1MIN(strlen(gH323ep.versionID), 
                                             sizeof(pVendor->versionId.data));
      memcpy(pVendor->versionId.data, gH323ep.versionID, 
                                                 pVendor->versionId.numocts); 
   }
}   


int ooGkClientReceive(ooGkClient *pGkClient)
{
   ASN1OCTET recvBuf[ASN_K_ENCBUFSIZ];
   int recvLen;
   char remoteHost[32];
   int iFromPort=0;
   OOCTXT *pctxt=NULL;
   H225RasMessage *pRasMsg=NULL;
   int iRet=OO_OK;
   
   ast_mutex_lock(&pGkClient->Lock);
   pctxt = &pGkClient->msgCtxt;

   recvLen = ooSocketRecvFrom(pGkClient->rasSocket, recvBuf, 2048, remoteHost,
                              32, &iFromPort);
   if(recvLen <0)
   {
      OOTRACEERR1("Error:Failed to receive RAS message\n");
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   OOTRACEDBGA1("GkClient Received RAS Message\n");
  
   /* Verify the gk */
   if(pGkClient->discoveryComplete)
   {
      if((strncmp(pGkClient->gkRasIP, remoteHost,strlen(pGkClient->gkRasIP)))||
         (pGkClient->gkRasPort!= iFromPort) )
      {
         OOTRACEWARN3("WARN:Ignoring message received from unknown gatekeeper "
                      "%s:%d\n", remoteHost, iFromPort);
	 ast_mutex_unlock(&pGkClient->Lock);
         return OO_OK;
      }
   }

   if(ASN_OK != setPERBuffer (pctxt, recvBuf, recvLen, TRUE ) )
   {
      OOTRACEERR1("Error:Failed to set PER buffer for RAS message decoding\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }      
   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR1("Error: Failed to allocate memory for RAS message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
#ifndef _COMPACT
   initializePrintHandler(&printHandler, "Received RAS Message");
   /* Add event handler to list */
   setEventHandler (pctxt, &printHandler);
#endif
   if(ASN_OK == asn1PD_H225RasMessage(pctxt, pRasMsg))
   {
#ifndef _COMPACT
      finishPrint();
      removeEventHandler(pctxt);
#endif
      iRet=ooGkClientHandleRASMessage( pGkClient, pRasMsg );
      if(iRet != OO_OK)
      {
         OOTRACEERR1("Error: Failed to handle received RAS message\n");
         pGkClient->state = GkClientFailed;
      }
      memReset(pctxt);
   }
   else{
      OOTRACEERR1("ERROR:Failed to decode received RAS message- ignoring"
                  "received message.\n");
#ifndef _COMPACT
      removeEventHandler(pctxt);
#endif
      memReset(pctxt);
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   ast_mutex_unlock(&pGkClient->Lock);
   return iRet;
}




/**
 * Manage incoming RAS message.
 */

int ooGkClientHandleRASMessage(ooGkClient *pGkClient, H225RasMessage *pRasMsg)
{
   int iRet = OO_OK;   
   switch( pRasMsg->t)
   {
   case T_H225RasMessage_gatekeeperConfirm:
      OOTRACEINFO1("Gatekeeper Confirmed (GCF) message received.\n");
      iRet = ooGkClientHandleGatekeeperConfirm(pGkClient, 
                                          pRasMsg->u.gatekeeperConfirm);
      break;
   case T_H225RasMessage_gatekeeperReject: 
      OOTRACEINFO1("Gatekeeper Reject (GRJ) message received\n");
      iRet = ooGkClientHandleGatekeeperReject(pGkClient, 
                                              pRasMsg->u.gatekeeperReject);
      break;
   case T_H225RasMessage_registrationConfirm:   
      OOTRACEINFO1("Registration Confirm (RCF) message received\n");
      iRet = ooGkClientHandleRegistrationConfirm(pGkClient,  
                                              pRasMsg->u.registrationConfirm );
      break;
   case T_H225RasMessage_registrationReject:
      OOTRACEINFO1("Registration Reject (RRJ) message received.\n");
      iRet = ooGkClientHandleRegistrationReject(pGkClient, 
                                                pRasMsg->u.registrationReject);
      break;
   case T_H225RasMessage_infoRequest:  
      //ooRasSendIRR( psRasMsg->sMessage.u.infoRequest->requestSeqNum );
      break;
   case T_H225RasMessage_admissionConfirm:
      OOTRACEINFO1("Admission Confirm (ACF) message received\n");
      iRet = ooGkClientHandleAdmissionConfirm(pGkClient, 
                                              pRasMsg->u.admissionConfirm);
      break;
   case T_H225RasMessage_unregistrationRequest:
      OOTRACEINFO1("UnRegistration Request (URQ) message received.\n");
      iRet = ooGkClientHandleUnregistrationRequest(pGkClient, 
                                            pRasMsg->u.unregistrationRequest);
      break;
   case T_H225RasMessage_unregistrationConfirm:
      OOTRACEINFO1("UnRegistration Confirm (UCF) message received.\n");
      break;
   case T_H225RasMessage_unregistrationReject:
      OOTRACEINFO1("UnRegistration Reject (URJ) message received.\n");
      break;
   case T_H225RasMessage_admissionReject:
      OOTRACEINFO1("Admission Reject (ARJ) message received.\n");
      iRet = ooGkClientHandleAdmissionReject(pGkClient, 
                                                   pRasMsg->u.admissionReject);
      break;
   case T_H225RasMessage_disengageConfirm:
      iRet = ooGkClientHandleDisengageConfirm(pGkClient, 
                                              pRasMsg->u.disengageConfirm);
      break;
   case T_H225RasMessage_disengageReject:
   case T_H225RasMessage_bandwidthConfirm:
   case T_H225RasMessage_bandwidthReject:
   case T_H225RasMessage_locationRequest:
   case T_H225RasMessage_locationConfirm:
   case T_H225RasMessage_locationReject:
   case T_H225RasMessage_infoRequestResponse:
   case T_H225RasMessage_nonStandardMessage:
   case T_H225RasMessage_unknownMessageResponse:
   case T_H225RasMessage_requestInProgress:
   case T_H225RasMessage_resourcesAvailableIndicate:
   case T_H225RasMessage_resourcesAvailableConfirm:
   case T_H225RasMessage_infoRequestAck:
   case T_H225RasMessage_infoRequestNak:
   case T_H225RasMessage_serviceControlIndication:
   case T_H225RasMessage_serviceControlResponse:
   case T_H225RasMessage_admissionConfirmSequence:
   default:
      /* Unhandled RAS message */
      iRet= OO_OK;
   }

   return iRet;
}

#ifndef _COMPACT
void ooGkClientPrintMessage
   (ooGkClient *pGkClient, ASN1OCTET *msg, ASN1UINT len)
{
   OOCTXT ctxt;
   H225RasMessage rasMsg;
   int ret; 

   initContext(&ctxt);
   setPERBuffer(&ctxt, msg, len, TRUE);
   initializePrintHandler(&printHandler, "Sending RAS Message");
   setEventHandler(&ctxt, &printHandler);

   ret = asn1PD_H225RasMessage(&ctxt, &rasMsg);
   if(ret != ASN_OK)
   {
      OOTRACEERR1("Error: Failed to decode RAS message\n");
   }
   finishPrint();
   freeContext(&ctxt);
}
#endif
/**
 * Encode and send RAS message.
 */

int ooGkClientSendMsg(ooGkClient *pGkClient, H225RasMessage *pRasMsg)
{
   ASN1OCTET msgBuf[MAXMSGLEN];
   ASN1OCTET *msgPtr=NULL;
   int  iLen;

   OOCTXT *pctxt = &pGkClient->msgCtxt;

   setPERBuffer( pctxt, msgBuf, MAXMSGLEN, TRUE );
   if ( ASN_OK == asn1PE_H225RasMessage(pctxt, pRasMsg) )
   {
      OOTRACEDBGC1("Ras message encoding - successful\n");
   }
   else {
      OOTRACEERR1("Error: RAS message encoding failed\n");
      return OO_FAILED;
   }

   msgPtr = encodeGetMsgPtr( pctxt, &iLen );
   /* If gatekeeper specified or have been discovered */
   if(pGkClient->gkMode == RasUseSpecificGatekeeper || 
      pGkClient->discoveryComplete)
   {
      if(ASN_OK != ooSocketSendTo( pGkClient->rasSocket, msgPtr, iLen, 
                                   pGkClient->gkRasIP, pGkClient->gkRasPort))
      {
         OOTRACEERR1("Error sending RAS message\n");
         return OO_FAILED;
      }
   }
   else if(pGkClient->gkMode == RasDiscoverGatekeeper && 
           !pGkClient->discoveryComplete) { 
      if(ASN_OK != ooSocketSendTo(pGkClient->rasSocket, msgPtr, iLen, 
                                       MULTICAST_GKADDRESS, MULTICAST_GKPORT))
      {
         OOTRACEERR1("Error sending multicast RAS message\n" );
         return OO_FAILED;
      }
   }
   else {/* should never go here */ 
      OOTRACEERR1("Error: GkClient in invalid state.\n");
      return OO_FAILED;
   }
#ifndef _COMPACT
   ooGkClientPrintMessage(pGkClient, msgPtr, iLen);
#endif
   return OO_OK;
}



int ooGkClientSendGRQ(ooGkClient *pGkClient)
{
   int iRet;
   H225RasMessage *pRasMsg=NULL;
   H225GatekeeperRequest *pGkReq=NULL;
   H225TransportAddress_ipAddress *pRasAddress;
   OOCTXT *pctxt = &pGkClient->msgCtxt;
   ooGkClientTimerCb *cbData=NULL;

   ast_mutex_lock(&pGkClient->Lock);

   /* Allocate memory for RAS message */
   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR1("Error: Memory allocation for GRQ RAS message failed\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   pGkReq = (H225GatekeeperRequest*)memAlloc(pctxt, 
                                                sizeof(H225GatekeeperRequest));
   if(!pGkReq)
   {
      OOTRACEERR1("Error:Memory allocation for GRQ failed\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memset(pGkReq, 0, sizeof(H225GatekeeperRequest));
   pRasMsg->t = T_H225RasMessage_gatekeeperRequest;
   pRasMsg->u.gatekeeperRequest = pGkReq;

   /* Populate message structure */
   pGkReq->requestSeqNum = pGkClient->requestSeqNum++;
   if ( !pGkReq->requestSeqNum )
      pGkReq->requestSeqNum = pGkClient->requestSeqNum++;

   pGkReq->protocolIdentifier = gProtocolID;
   pGkReq->m.nonStandardDataPresent=0;
   pGkReq->rasAddress.t=T_H225TransportAddress_ipAddress; /* IPv4 address */
   pRasAddress = (H225TransportAddress_ipAddress*)memAlloc(pctxt, 
                                      sizeof(H225TransportAddress_ipAddress));
   if(!pRasAddress)
   {
      OOTRACEERR1("Error: Memory allocation for Ras Address of GRQ message "
                  "failed\n");
      memReset(&pGkClient->msgCtxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

 
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pRasAddress->ip.data);

   pRasAddress->ip.numocts = 4;
   pRasAddress->port = pGkClient->localRASPort;
   pGkReq->rasAddress.u.ipAddress = pRasAddress;

   /* Pose as gateway or terminal as per config */
   if(gH323ep.isGateway)
      pGkReq->endpointType.m.gatewayPresent = TRUE;
   else
      pGkReq->endpointType.m.terminalPresent = TRUE;

   pGkReq->endpointType.m.nonStandardDataPresent=0;
   pGkReq->endpointType.m.vendorPresent=1;

   ooGkClientFillVendor(pGkClient, &pGkReq->endpointType.vendor);


   pGkReq->m.endpointAliasPresent=TRUE;
   if(OO_OK != ooPopulateAliasList(&pGkClient->msgCtxt, gH323ep.aliases, 
                                      &pGkReq->endpointAlias, 0))
   {
      OOTRACEERR1("Error Failed to fill alias information for GRQ message\n");
      memReset(&pGkClient->msgCtxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error: Failed to send GRQ message\n");
      memReset(&pGkClient->msgCtxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   OOTRACEINFO1("Sent GRQ message\n");
   cbData = (ooGkClientTimerCb*) memAlloc
                               (&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
   if(!cbData)
   {
      OOTRACEERR1("Error:Failed to allocate memory to GRQ timer callback\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   cbData->timerType = OO_GRQ_TIMER;
   cbData->pGkClient = pGkClient;
   if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList, 
                     &ooGkClientGRQTimerExpired, pGkClient->grqTimeout, 
                     cbData, FALSE))      
   {
      OOTRACEERR1("Error:Unable to create GRQ timer.\n ");
      memFreePtr(&pGkClient->ctxt, cbData);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}

int ooGkClientHandleGatekeeperReject
   (ooGkClient *pGkClient, H225GatekeeperReject *pGatekeeperReject)
{
   unsigned int x=0;
   DListNode *pNode = NULL;
   OOTimer *pTimer = NULL;

   if(pGkClient->gkMode == RasUseSpecificGatekeeper)
   {
      /* delete the corresponding GRQ timer */
      for(x=0; x<pGkClient->timerList.count; x++)
      {
         pNode =  dListFindByIndex(&pGkClient->timerList, x);
         pTimer = (OOTimer*)pNode->data;
         if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_GRQ_TIMER)
         {
            memFreePtr(&pGkClient->ctxt, pTimer->cbData);
            ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
            OOTRACEDBGA1("Deleted GRQ Timer.\n");
            break;
         }
      }
 
      pGkClient->state = GkClientGkErr;
      switch(pGatekeeperReject->rejectReason.t)
      {
      case T_H225GatekeeperRejectReason_resourceUnavailable:
         OOTRACEERR1("Error: Gatekeeper Reject - Resource Unavailable\n");
         break;
      case T_H225GatekeeperRejectReason_terminalExcluded:
         OOTRACEERR1("Error: Gatekeeper Reject - Terminal Excluded\n");
         break;
      case T_H225GatekeeperRejectReason_invalidRevision:
         OOTRACEERR1("Error: Gatekeeper Reject - Invalid Revision\n");
         break;
      case T_H225GatekeeperRejectReason_undefinedReason:
         OOTRACEERR1("Error: Gatekeeper Reject - Undefined Reason\n");
         break;
      case T_H225GatekeeperRejectReason_securityDenial:
         OOTRACEERR1("Error: Gatekeeper Reject - Security Denial\n");
         break;
      case T_H225GatekeeperRejectReason_genericDataReason:
         OOTRACEERR1("Error: Gatekeeper Reject - Generic Data Reason\n");
         break;
      case T_H225GatekeeperRejectReason_neededFeatureNotSupported:
         OOTRACEERR1("Error: Gatekeeper Reject - Needed Feature Not "
                     "Supported\n");
         break;
      case T_H225GatekeeperRejectReason_securityError:
         OOTRACEERR1("Error:Gatekeeper Reject - Security Error\n");
         break;
      default:
         OOTRACEERR1("Error: Gatekeeper Reject - Invalid reason\n");
      }
      return OO_OK;
   }
   OOTRACEDBGB1("Gatekeeper Reject response received for multicast GRQ "
                "request\n");
   return OO_OK;

}

int ooGkClientHandleGatekeeperConfirm
   (ooGkClient *pGkClient, H225GatekeeperConfirm *pGatekeeperConfirm)
{
   int iRet=0;
   unsigned int x=0;
   DListNode *pNode = NULL;
   OOTimer *pTimer = NULL;
   H225TransportAddress_ipAddress *pRasAddress;

   if(pGkClient->discoveryComplete)
   {
      OOTRACEDBGB1("Ignoring GKConfirm as Gatekeeper has been discovered\n");
      return OO_OK;
   }

   if(pGatekeeperConfirm->m.gatekeeperIdentifierPresent) 
   {
      pGkClient->gkId.nchars = pGatekeeperConfirm->gatekeeperIdentifier.nchars;
      if (pGkClient->gkId.data) {
         memFreePtr(&pGkClient->ctxt, pGkClient->gkId.data);
      }
      pGkClient->gkId.data = (ASN116BITCHAR*)memAlloc(&pGkClient->ctxt,
                              sizeof(ASN116BITCHAR)*pGkClient->gkId.nchars);
      if(!pGkClient->gkId.data)
      {
         OOTRACEERR1("Error:Failed to allocate memory for GK ID data\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }

      memcpy(pGkClient->gkId.data, 
             pGatekeeperConfirm->gatekeeperIdentifier.data,
             sizeof(ASN116BITCHAR)* pGkClient->gkId.nchars);
   }
   else{
      OOTRACEINFO1("ERROR:No Gatekeeper ID present in received GKConfirmed "
                  "message\n");
      pGkClient->gkId.nchars = 0;
   }
   
   /* Extract Gatekeeper's RAS address */
   if(pGatekeeperConfirm->rasAddress.t != T_H225TransportAddress_ipAddress)
   {
      OOTRACEERR1("ERROR:Unsupported RAS address type in received Gk Confirm"
                  " message.\n");
      pGkClient->state = GkClientGkErr;
      return OO_FAILED;
   }
   pRasAddress =   pGatekeeperConfirm->rasAddress.u.ipAddress;
   sprintf(pGkClient->gkRasIP, "%d.%d.%d.%d", pRasAddress->ip.data[0],
                                              pRasAddress->ip.data[1],
                                              pRasAddress->ip.data[2], 
                                              pRasAddress->ip.data[3]);
   pGkClient->gkRasPort = pRasAddress->port;
   
   pGkClient->discoveryComplete = TRUE;
   pGkClient->state = GkClientDiscovered;
   OOTRACEINFO1("Gatekeeper Confirmed\n");


   /* Delete the corresponding GRQ timer */
   for(x=0; x<pGkClient->timerList.count; x++)
   {
      pNode =  dListFindByIndex(&pGkClient->timerList, x);
      pTimer = (OOTimer*)pNode->data;
      if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_GRQ_TIMER)
      {
         memFreePtr(&pGkClient->ctxt, pTimer->cbData);
         ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
         OOTRACEDBGA1("Deleted GRQ Timer.\n");
      }
   }

   iRet = ooGkClientSendRRQ(pGkClient, FALSE);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send initial RRQ\n");
      return OO_FAILED;
   }
   return OO_OK;
}

/**
 * Send RRQ.
 */

int ooGkClientSendRRQ(ooGkClient *pGkClient, ASN1BOOL keepAlive)
{
   int iRet;
   H225RasMessage *pRasMsg=NULL;
   H225RegistrationRequest *pRegReq=NULL;
   OOCTXT *pctxt=NULL;
   H225TransportAddress *pTransportAddress=NULL;
   H225TransportAddress_ipAddress *pIpAddress=NULL;
   ooGkClientTimerCb *cbData =NULL;
   H225SupportedProtocols *pProtocol = NULL;
   H225VoiceCaps *pVoiceCaps = NULL;

   ast_mutex_lock(&pGkClient->Lock);

   pctxt = &pGkClient->msgCtxt;

   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR1("Error: Memory allocation for RRQ RAS message failed\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   pRegReq = (H225RegistrationRequest*)memAlloc(pctxt, 
                                          sizeof(H225RegistrationRequest));
   if(!pRegReq)
   {
      OOTRACEERR1("Error:Memory allocation for RRQ failed\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memset(pRegReq, 0, sizeof(H225RegistrationRequest));
   pRasMsg->t = T_H225RasMessage_registrationRequest;
   pRasMsg->u.registrationRequest = pRegReq;
   
   pRegReq->protocolIdentifier = gProtocolID;
   pRegReq->m.nonStandardDataPresent=0;
   /* Populate CallSignal Address List*/
   pTransportAddress = (H225TransportAddress*) memAlloc(pctxt, 
                                                 sizeof(H225TransportAddress));
   pIpAddress = (H225TransportAddress_ipAddress*) memAlloc(pctxt,
                                       sizeof(H225TransportAddress_ipAddress));
   if(!pTransportAddress || !pIpAddress)
   {
      OOTRACEERR1("Error:Failed to allocate memory for signalling address of "
                  "RRQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pTransportAddress->t = T_H225TransportAddress_ipAddress;
   pTransportAddress->u.ipAddress = pIpAddress;
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pIpAddress->ip.data);
   pIpAddress->ip.numocts = 4;
   pIpAddress->port = gH323ep.listenPort;
   
   dListInit(&pRegReq->callSignalAddress);
   dListAppend(pctxt, &pRegReq->callSignalAddress, 
                                       (void*)pTransportAddress);

   /* Populate RAS Address List*/
   pTransportAddress = NULL;
   pIpAddress = NULL;
   pTransportAddress = (H225TransportAddress*) memAlloc(pctxt, 
                                                 sizeof(H225TransportAddress));
   pIpAddress = (H225TransportAddress_ipAddress*) memAlloc(pctxt,
                                       sizeof(H225TransportAddress_ipAddress));
   if(!pTransportAddress || !pIpAddress)
   {
      OOTRACEERR1("Error:Failed to allocate memory for RAS address of "
                  "RRQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   pTransportAddress->t = T_H225TransportAddress_ipAddress;
   pTransportAddress->u.ipAddress = pIpAddress;
   
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pIpAddress->ip.data);

   pIpAddress->ip.numocts = 4;
   pIpAddress->port = pGkClient->localRASPort;
   
   dListInit(&pRegReq->rasAddress);
   dListAppend(pctxt, &pRegReq->rasAddress, 
                                       (void*)pTransportAddress);
   
   /* Pose as gateway or terminal as per config */
   if(gH323ep.isGateway)
      pRegReq->terminalType.m.gatewayPresent = TRUE;
   else
      pRegReq->terminalType.m.terminalPresent = TRUE;

   pRegReq->terminalType.m.vendorPresent=TRUE;
   ooGkClientFillVendor(pGkClient, &pRegReq->terminalType.vendor );

   if (gH323ep.isGateway) {
      pRegReq->terminalType.gateway.m.protocolPresent = TRUE;
      pProtocol = (H225SupportedProtocols*) memAlloc(pctxt,
                                       sizeof(H225SupportedProtocols));
      pVoiceCaps = (H225VoiceCaps*) memAlloc(pctxt, sizeof(H225VoiceCaps));
      if(!pProtocol || !pVoiceCaps) {
      	OOTRACEERR1("Error:Failed to allocate memory for protocol info of "
                  "RRQ message\n");
      	memReset(pctxt);
      	pGkClient->state = GkClientFailed;
      	ast_mutex_unlock(&pGkClient->Lock);
      	return OO_FAILED;
      }

      memset(pVoiceCaps, 0, sizeof(H225VoiceCaps));
      memset(pProtocol, 0, sizeof(H225SupportedProtocols));

      pVoiceCaps->m.supportedPrefixesPresent = TRUE;
      ooPopulatePrefixList(pctxt, gH323ep.aliases, &pVoiceCaps->supportedPrefixes);

      pProtocol->t = T_H225SupportedProtocols_voice;
      pProtocol->u.voice = pVoiceCaps;
   
      dListInit(&pRegReq->terminalType.gateway.protocol);
      dListAppend(pctxt, &pRegReq->terminalType.gateway.protocol, 
                                       (void*)pProtocol);

   }

   pRegReq->m.terminalAliasPresent=TRUE;
   if(OO_OK != ooPopulateAliasList(pctxt, gH323ep.aliases, 
                                     &pRegReq->terminalAlias, 0)) {
     OOTRACEERR1("Error filling alias for RRQ\n");
     memReset(pctxt); 
     pGkClient->state = GkClientFailed;
     ast_mutex_unlock(&pGkClient->Lock);
     return OO_FAILED;
   }
   
   if (pGkClient->gkId.nchars) {
    pRegReq->m.gatekeeperIdentifierPresent=TRUE;
    pRegReq->gatekeeperIdentifier.nchars = pGkClient->gkId.nchars;
    pRegReq->gatekeeperIdentifier.data = (ASN116BITCHAR*)memAlloc
                         (pctxt, pGkClient->gkId.nchars*sizeof(ASN116BITCHAR));
    if(!pRegReq->gatekeeperIdentifier.data)
    {
      OOTRACEERR1("Error: Failed to allocate memory for GKIdentifier in RRQ "
                   "message.\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
    }
    memcpy(pRegReq->gatekeeperIdentifier.data, pGkClient->gkId.data, 
                                pGkClient->gkId.nchars*sizeof(ASN116BITCHAR));
   }
   
   ooGkClientFillVendor(pGkClient, &pRegReq->endpointVendor);
   
   pRegReq->m.willSupplyUUIEsPresent=TRUE;
   pRegReq->willSupplyUUIEs=FALSE;

   pRegReq->requestSeqNum = pGkClient->requestSeqNum++;
   if(!pRegReq->requestSeqNum)
      pRegReq->requestSeqNum = pGkClient->requestSeqNum++;
   
   pRegReq->discoveryComplete= pGkClient->discoveryComplete;
   pRegReq->m.keepAlivePresent=TRUE;
   pRegReq->keepAlive= keepAlive;

   /*
    * Cisco Gatekeeper re-registration fix.  Thanks to Mike Tubby (mike@tubby.org) 28feb2007
    * Without this patch initial registration works, but re-registration fails!
    *
    * For light-weight re-registration, keepalive is set true
    * GK needs rasAddress, keepAlive, endpointIdentifier, gatekeeperIdentifier,
    * tokens, and timeToLive
    * GK will ignore all other params if KeepAlive is set.
    *
    */
   if(keepAlive) {
      /* KeepAlive, re-registration message...
         allocate storage for endpoint-identifier, and populate it from what the
         GK told us from the previous RCF. Only allocate on the first pass thru here */
      pRegReq->endpointIdentifier.data = 
           (ASN116BITCHAR*)memAlloc(pctxt, pGkClient->endpointId.nchars*sizeof(ASN116BITCHAR));
      if (pRegReq->endpointIdentifier.data) {
         pRegReq->endpointIdentifier.nchars = pGkClient->endpointId.nchars;
         pRegReq->m.endpointIdentifierPresent = TRUE;
         memcpy(pRegReq->endpointIdentifier.data, pGkClient->endpointId.data, pGkClient->endpointId.nchars*sizeof(ASN116BITCHAR));
         OOTRACEINFO1("Sending RRQ for re-registration (with EndpointID)\n");
      }
      else {
         OOTRACEERR1("Error: Failed to allocate memory for EndpointIdentifier in RRQ \n");
         memReset(pctxt);
         pGkClient->state = GkClientFailed;
	 ast_mutex_unlock(&pGkClient->Lock);
         return OO_FAILED;
      }
   }

   pRegReq->m.timeToLivePresent = TRUE;
   pRegReq->timeToLive = pGkClient->regTimeout;

   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error: Failed to send RRQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   OOTRACEINFO1("Sent RRQ message \n");
   /* Start RRQ Timer */
   cbData = (ooGkClientTimerCb*) memAlloc
                            (&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
   if(!cbData)
   {
      OOTRACEERR1("Error:Failed to allocate memory to RRQ timer callback\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   cbData->timerType = OO_RRQ_TIMER;
   cbData->pGkClient = pGkClient;
   if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList, 
                     &ooGkClientRRQTimerExpired, pGkClient->rrqTimeout, 
                     cbData, FALSE))      
   {
      OOTRACEERR1("Error:Unable to create GRQ timer.\n ");
      memFreePtr(&pGkClient->ctxt, cbData);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   
   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}



/**
 * Manage incoming RCF message.
 */

int ooGkClientHandleRegistrationConfirm
   (ooGkClient *pGkClient, H225RegistrationConfirm *pRegistrationConfirm)
{
   int i=0;
   unsigned int x=0;
   OOTimer *pTimer = NULL;
   DListNode *pNode = NULL;
   H225TransportAddress *pCallSigAddr=NULL;
   ooGkClientTimerCb *cbData;
   ASN1UINT regTTL=0;
   /* Extract Endpoint Id */
   if (pGkClient->endpointId.data)
	memFreePtr(&pGkClient->ctxt, pGkClient->endpointId.data);
   pGkClient->endpointId.nchars = 
                              pRegistrationConfirm->endpointIdentifier.nchars;
   pGkClient->endpointId.data = (ASN116BITCHAR*)memAlloc(&pGkClient->ctxt,
                          sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);
   if(!pGkClient->endpointId.data)
   {
      OOTRACEERR1("Error:Failed to allocate memory for endpoint Id.\n");
      pGkClient->state = GkClientFailed;
      return OO_FAILED;
   }
   
   memcpy(pGkClient->endpointId.data, 
          pRegistrationConfirm->endpointIdentifier.data,
          sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);

   /* Extract GK Identifier */
   
   if(pRegistrationConfirm->m.gatekeeperIdentifierPresent && pGkClient->gkId.nchars == 0)
   {
      pGkClient->gkId.nchars = pRegistrationConfirm->gatekeeperIdentifier.nchars;
      pGkClient->gkId.data = (ASN116BITCHAR*)memAlloc(&pGkClient->ctxt,
                              sizeof(ASN116BITCHAR)*pGkClient->gkId.nchars);
      if(!pGkClient->gkId.data)
      {
         OOTRACEERR1("Error:Failed to allocate memory for GK ID data\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }

      memcpy(pGkClient->gkId.data, 
             pRegistrationConfirm->gatekeeperIdentifier.data,
             sizeof(ASN116BITCHAR)* pGkClient->gkId.nchars);
   }

   /* Extract CallSignalling Address */
   for(i=0; i<(int)pRegistrationConfirm->callSignalAddress.count; i++)
   {
      pNode = dListFindByIndex(&pRegistrationConfirm->callSignalAddress, i);
      if(!pNode)
      {
         OOTRACEERR1("Error:Invalid Registration confirmed message\n");
         OOTRACEINFO1("Ignoring RCF, will retransmit RRQ after timeout\n");
         return OO_FAILED; 
      }
      pCallSigAddr = (H225TransportAddress*)pNode->data;
      if(pCallSigAddr->t != T_H225TransportAddress_ipAddress)
         continue;
      sprintf(pGkClient->gkCallSignallingIP, "%d.%d.%d.%d", 
                            pCallSigAddr->u.ipAddress->ip.data[0],
                            pCallSigAddr->u.ipAddress->ip.data[1],
                            pCallSigAddr->u.ipAddress->ip.data[2],
                            pCallSigAddr->u.ipAddress->ip.data[3]);
      pGkClient->gkCallSignallingPort = pCallSigAddr->u.ipAddress->port;
   }
   
   /* Update list of registered aliases*/
   if(pRegistrationConfirm->m.terminalAliasPresent)
   {
      ooGkClientUpdateRegisteredAliases(pGkClient, 
                                   &pRegistrationConfirm->terminalAlias, TRUE);
   }
   else{/* Everything registered*/
     ooGkClientUpdateRegisteredAliases(pGkClient, NULL, TRUE);
   }

   /* Is keepAlive supported */
   if(pRegistrationConfirm->m.timeToLivePresent)
   {
      pGkClient->regTimeout = pRegistrationConfirm->timeToLive;
      OOTRACEINFO2("Gatekeeper supports KeepAlive, Registration TTL is %d\n",
                    pRegistrationConfirm->timeToLive);

      if(pGkClient->regTimeout > DEFAULT_TTL_OFFSET)
         regTTL = pGkClient->regTimeout - DEFAULT_TTL_OFFSET;
      else {
         regTTL = pGkClient->regTimeout - 1; /* -1 due to some ops expire us few earlier */
	 if (regTTL <= 0)
		regTTL = 1;
      }

      cbData = (ooGkClientTimerCb*) memAlloc
                                (&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
      if(!cbData)
      {
         OOTRACEERR1("Error:Failed to allocate memory for Regisration timer."
                     "\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }
      cbData->timerType = OO_REG_TIMER;
      cbData->pGkClient = pGkClient;
      if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList, 
                     &ooGkClientREGTimerExpired, regTTL, 
                     cbData, FALSE))
      {
         OOTRACEERR1("Error:Unable to create REG timer.\n ");
         memFreePtr(&pGkClient->ctxt, cbData);
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }    
      
   }
   else{
      pGkClient->regTimeout = 0;
      OOTRACEINFO1("Gatekeeper does not support KeepAlive.\n");
   }
   /* Extract Pre-Granted ARQ */
   if(pRegistrationConfirm->m.preGrantedARQPresent)
   {
      memcpy(&pGkClient->gkInfo.preGrantedARQ, 
             &pRegistrationConfirm->preGrantedARQ,
             sizeof(H225RegistrationConfirm_preGrantedARQ));
   }


   /* First delete the corresponding RRQ timer */
   pNode = NULL;
   for(x=0; x<pGkClient->timerList.count; x++)
   {
      pNode =  dListFindByIndex(&pGkClient->timerList, x);
      pTimer = (OOTimer*)pNode->data;
      if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_RRQ_TIMER)
      {
         memFreePtr(&pGkClient->ctxt, pTimer->cbData);
         ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
         OOTRACEDBGA1("Deleted RRQ Timer.\n");
      }
   }
   pGkClient->state = GkClientRegistered;
   if(pGkClient->callbacks.onReceivedRegistrationConfirm)
      pGkClient->callbacks.onReceivedRegistrationConfirm(pRegistrationConfirm,
                                                              gH323ep.aliases);
   return OO_OK;
}

int ooGkClientHandleRegistrationReject
   (ooGkClient *pGkClient, H225RegistrationReject *pRegistrationReject)
{
   int iRet=0;
   unsigned int x=0;
   DListNode *pNode = NULL;
   OOTimer *pTimer = NULL;
   ooGkClientTimerCb *cbData=NULL;

   /* First delete the corresponding RRQ timer */
   for(x=0; x<pGkClient->timerList.count; x++)
   {
      pNode =  dListFindByIndex(&pGkClient->timerList, x);
      pTimer = (OOTimer*)pNode->data;
      if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_RRQ_TIMER)
      {
         memFreePtr(&pGkClient->ctxt, pTimer->cbData);
         ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
         OOTRACEDBGA1("Deleted RRQ Timer.\n");
      }
   }

   switch(pRegistrationReject->rejectReason.t)
   {
   case T_H225RegistrationRejectReason_discoveryRequired:
      OOTRACEINFO1("RRQ Rejected - Discovery Required\n");

      pGkClient->discoveryComplete = FALSE;
      pGkClient->state = GkClientIdle;
      pGkClient->rrqRetries = 0;
      pGkClient->grqRetries = 0;
      if(OO_OK != ooGkClientSendGRQ(pGkClient))
      {
         OOTRACEERR1("Error:Failed to send GRQ message\n");
         return OO_FAILED;
      }
      return OO_OK;
   case T_H225RegistrationRejectReason_invalidRevision:
      OOTRACEERR1("RRQ Rejected - Invalid Revision\n");
      break;
   case T_H225RegistrationRejectReason_invalidCallSignalAddress:
      OOTRACEERR1("RRQ Rejected - Invalid CallSignalAddress\n");
      break;
   case T_H225RegistrationRejectReason_invalidRASAddress:
      OOTRACEERR1("RRQ Rejected - Invalid RAS Address\n");
      break;
   case T_H225RegistrationRejectReason_duplicateAlias:
      OOTRACEERR1("RRQ Rejected - Duplicate Alias\n");
      break;
   case T_H225RegistrationRejectReason_invalidTerminalType:
      OOTRACEERR1("RRQ Rejected - Invalid Terminal Type\n");
      break;
   case T_H225RegistrationRejectReason_undefinedReason:
      OOTRACEERR1("RRQ Rejected - Undefined Reason\n");
      break;
   case T_H225RegistrationRejectReason_transportNotSupported:
      OOTRACEERR1("RRQ Rejected - Transport Not supported\n");
      break;
   case T_H225RegistrationRejectReason_transportQOSNotSupported:
      OOTRACEERR1("RRQ Rejected - Transport QOS Not Supported\n");
      break;
   case T_H225RegistrationRejectReason_resourceUnavailable:
      OOTRACEERR1("RRQ Rejected - Resource Unavailable\n");
      break;
   case T_H225RegistrationRejectReason_invalidAlias:
      OOTRACEERR1("RRQ Rejected - Invalid Alias\n");
      break;
   case T_H225RegistrationRejectReason_securityDenial:
      OOTRACEERR1("RRQ Rejected - Security Denial\n");
      break;
   case T_H225RegistrationRejectReason_fullRegistrationRequired:
      OOTRACEINFO1("RRQ Rejected - Full Registration Required\n");
      pGkClient->state = GkClientDiscovered;
      pGkClient->rrqRetries = 0;
      iRet = ooGkClientSendRRQ(pGkClient, 0); /* No keepAlive */
      if(iRet != OO_OK){
         OOTRACEERR1("\nError: Full Registration transmission failed\n");
         return OO_FAILED;
      }
      return OO_OK;
   case T_H225RegistrationRejectReason_additiveRegistrationNotSupported:
      OOTRACEERR1("RRQ Rejected - Additive Registration Not Supported\n");
      break;
   case T_H225RegistrationRejectReason_invalidTerminalAliases:
      OOTRACEERR1("RRQ Rejected - Invalid Terminal Aliases\n");
      break;
   case T_H225RegistrationRejectReason_genericDataReason:
      OOTRACEERR1("RRQ Rejected - Generic Data Reason\n");
      break;
   case T_H225RegistrationRejectReason_neededFeatureNotSupported:
      OOTRACEERR1("RRQ Rejected - Needed Feature Not Supported\n");
      break;
   case T_H225RegistrationRejectReason_securityError:
      OOTRACEERR1("RRQ Rejected - Security Error\n");
      break;
   default:
      OOTRACEINFO1("RRQ Rejected - Invalid Reason\n");
   }

   /* send again GRQ/RRQ's */
   ast_mutex_lock(&pGkClient->Lock);
   pGkClient->state = GkClientUnregistered;
   pGkClient->rrqRetries = 0;
   pGkClient->grqRetries = 0;
   pGkClient->discoveryComplete = FALSE;

   cbData = (ooGkClientTimerCb*) memAlloc
                               (&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
   if(!cbData)
   {
      OOTRACEERR1("Error:Failed to allocate memory to GRQ timer callback\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   cbData->timerType = OO_GRQ_TIMER;
   cbData->pGkClient = pGkClient;
   if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList,
                     &ooGkClientGRQTimerExpired, pGkClient->grqTimeout,
                     cbData, FALSE))
   {
      OOTRACEERR1("Error:Unable to create GRQ timer.\n ");
      memFreePtr(&pGkClient->ctxt, cbData);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   ast_mutex_unlock(&pGkClient->Lock);

   return OO_OK;

}


int ooGkClientSendURQ(ooGkClient *pGkClient, ooAliases *aliases)
{
   int iRet;
   H225RasMessage *pRasMsg=NULL;
   H225UnregistrationRequest *pUnregReq=NULL;
   OOCTXT *pctxt=NULL;
   H225TransportAddress *pTransportAddress=NULL;
   H225TransportAddress_ipAddress *pIpAddress=NULL;

   ast_mutex_lock(&pGkClient->Lock);
   pctxt = &pGkClient->msgCtxt;

   OOTRACEDBGA1("Building Unregistration Request message\n");

   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR1("Error: Memory allocation for URQ RAS message failed\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   pUnregReq = (H225UnregistrationRequest*)memAlloc(pctxt, 
                                          sizeof(H225UnregistrationRequest));
   if(!pUnregReq)
   {
      OOTRACEERR1("Error:Memory allocation for URQ failed\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memset(pUnregReq, 0, sizeof(H225UnregistrationRequest));
   pRasMsg->t = T_H225RasMessage_unregistrationRequest;
   pRasMsg->u.unregistrationRequest = pUnregReq;

   pUnregReq->requestSeqNum = pGkClient->requestSeqNum++;
   if(!pUnregReq->requestSeqNum)
      pUnregReq->requestSeqNum = pGkClient->requestSeqNum++;

   

 /* Populate CallSignal Address List*/
   pTransportAddress = (H225TransportAddress*) memAlloc(pctxt, 
                                                 sizeof(H225TransportAddress));
   pIpAddress = (H225TransportAddress_ipAddress*) memAlloc(pctxt,
                                       sizeof(H225TransportAddress_ipAddress));
   if(!pTransportAddress || !pIpAddress)
   {
      OOTRACEERR1("Error:Failed to allocate memory for signalling address of "
                  "RRQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pTransportAddress->t = T_H225TransportAddress_ipAddress;
   pTransportAddress->u.ipAddress = pIpAddress;
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pIpAddress->ip.data);
   pIpAddress->ip.numocts = 4;
   pIpAddress->port = gH323ep.listenPort;
   
   dListInit(&pUnregReq->callSignalAddress);
   dListAppend(pctxt, &pUnregReq->callSignalAddress, 
                                       (void*)pTransportAddress);

   /* Populate Endpoint Identifier */
   pUnregReq->m.endpointIdentifierPresent = TRUE;
   pUnregReq->endpointIdentifier.nchars = pGkClient->endpointId.nchars;
   pUnregReq->endpointIdentifier.data = (ASN116BITCHAR*)memAlloc(pctxt,
                           sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);
   if(!pUnregReq->endpointIdentifier.data)
   {
      OOTRACEERR1("Error: Failed to allocate memory for EndPoint Id in URQ "
                  "message.\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memcpy((void*)pUnregReq->endpointIdentifier.data, 
          (void*)pGkClient->endpointId.data,
          sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);

   /* Populate gatekeeper identifier */
   if (pGkClient->gkId.nchars) {
    pUnregReq->m.gatekeeperIdentifierPresent = TRUE;
    pUnregReq->gatekeeperIdentifier.nchars = pGkClient->gkId.nchars;
    pUnregReq->gatekeeperIdentifier.data = (ASN116BITCHAR*)memAlloc(pctxt,
                                 sizeof(ASN116BITCHAR)*pGkClient->gkId.nchars);
    if(!pUnregReq->gatekeeperIdentifier.data)
    {
      OOTRACEERR1("Error:Failed to allocate memory for GKID of URQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
    }
    memcpy((void*)pUnregReq->gatekeeperIdentifier.data, 
          (void*)pGkClient->gkId.data, 
          sizeof(ASN116BITCHAR)*pGkClient->gkId.nchars);   
   }

   /* Check whether specific aliases are to be unregistered*/
   if(aliases)
   {
      pUnregReq->m.endpointAliasPresent = TRUE;
      ooPopulateAliasList(pctxt, aliases, &pUnregReq->endpointAlias, 0);
   }

  
   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send UnregistrationRequest message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pGkClient->state = GkClientUnregistered;
   OOTRACEINFO1("Unregistration Request message sent.\n");

   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}               



int ooGkClientHandleUnregistrationRequest
   (ooGkClient *pGkClient, H225UnregistrationRequest * punregistrationRequest)
{
   int iRet=0, x;
   OOTimer *pTimer = NULL;
   DListNode *pNode = NULL;
 
   /* Lets first send unregistration confirm message back to gatekeeper*/
   ooGkClientSendUnregistrationConfirm(pGkClient, 
                                      punregistrationRequest->requestSeqNum);

   if(punregistrationRequest->m.endpointAliasPresent)
   {
      OOTRACEINFO1("Gatekeeper requested a list of aliases be unregistered\n");
      ooGkClientUpdateRegisteredAliases(pGkClient, 
                                &punregistrationRequest->endpointAlias, FALSE);
   }
   else{

      OOTRACEINFO1("Gatekeeper requested a all aliases to be unregistered\n");
      ooGkClientUpdateRegisteredAliases(pGkClient, NULL, FALSE);
      /* Send a fresh Registration request and if that fails, go back to
         Gatekeeper discovery.
      */
      OOTRACEINFO1("Sending fresh RRQ - as unregistration request received\n");
      pGkClient->rrqRetries = 0;
      pGkClient->state = GkClientDiscovered;


      /* delete the corresponding RRQ & REG timers */
	pNode = NULL;
	for(x=0; x<pGkClient->timerList.count; x++) {
		pNode =  dListFindByIndex(&pGkClient->timerList, x);
		pTimer = (OOTimer*)pNode->data;
		if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_RRQ_TIMER) {
         		memFreePtr(&pGkClient->ctxt, pTimer->cbData);
         		ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
         		OOTRACEDBGA1("Deleted RRQ Timer.\n");
      		}
		if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_REG_TIMER) {
         		memFreePtr(&pGkClient->ctxt, pTimer->cbData);
         		ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
         		OOTRACEDBGA1("Deleted REG Timer.\n");
      		}
 	}

      iRet = ooGkClientSendRRQ(pGkClient, 0); 
      if(iRet != OO_OK)
      {
         OOTRACEERR1("Error: Failed to send RRQ message\n");
         return OO_FAILED;
      }
   }


   if(pGkClient->callbacks.onReceivedUnregistrationRequest)
      pGkClient->callbacks.onReceivedUnregistrationRequest(
                                      punregistrationRequest, gH323ep.aliases);
   return OO_OK;
}

int ooGkClientSendUnregistrationConfirm(ooGkClient *pGkClient, unsigned reqNo)
{
   int iRet = OO_OK;
   OOCTXT *pctxt = &pGkClient->msgCtxt;   
   H225RasMessage *pRasMsg=NULL;
   H225UnregistrationConfirm *pUCF=NULL;

   ast_mutex_lock(&pGkClient->Lock);

   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   pUCF = (H225UnregistrationConfirm*)memAlloc(pctxt, 
                                           sizeof(H225UnregistrationConfirm));
   if(!pRasMsg || !pUCF)
   {
      OOTRACEERR1("Error: Memory allocation for UCF RAS message failed\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pRasMsg->t = T_H225RasMessage_unregistrationConfirm;
   pRasMsg->u.unregistrationConfirm = pUCF;
   memset(pUCF, 0, sizeof(H225UnregistrationConfirm));
   
   pUCF->requestSeqNum = reqNo;
   
   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send UnregistrationConfirm message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   OOTRACEINFO1("Unregistration Confirm message sent for \n");
   memReset(pctxt);

   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}




int ooGkClientSendAdmissionRequest
   (ooGkClient *pGkClient, OOH323CallData *call, ASN1BOOL retransmit)
{
   int iRet = 0;
   unsigned int x;
   DListNode *pNode;
   ooGkClientTimerCb *cbData=NULL;
   H225RasMessage *pRasMsg=NULL;
   OOCTXT* pctxt;
   H225AdmissionRequest *pAdmReq=NULL;
   H225TransportAddress_ipAddress *pIpAddressLocal =NULL, *pIpAddressRemote=NULL;
   ooAliases *destAliases = NULL, *srcAliases=NULL;
   RasCallAdmissionInfo *pCallAdmInfo=NULL;
   pctxt = &pGkClient->msgCtxt;

   ast_mutex_lock(&pGkClient->Lock);

   OOTRACEDBGA3("Building Admission Request for call (%s, %s)\n", 
                 call->callType, call->callToken);   
   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR3("Error:Memory - ooGkClientSendAdmissionRequest - "
                  "pRasMsg(%s, %s)\n", call->callType, call->callToken);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pRasMsg->t = T_H225RasMessage_admissionRequest;
   pAdmReq = (H225AdmissionRequest*) memAlloc(pctxt, 
                                                 sizeof(H225AdmissionRequest));
   if(!pAdmReq)
   {
      OOTRACEERR3("Error:Memory - ooGkClientSendAdmissionRequest - "
                  "pAdmReq(%s, %s)\n", call->callType, call->callToken);
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memset(pAdmReq, 0, sizeof(H225AdmissionRequest));
   pRasMsg->u.admissionRequest = pAdmReq;
   
   /* Populate call signalling addresses */
   pIpAddressLocal = (H225TransportAddress_ipAddress*)memAlloc(pctxt, 
                                     sizeof(H225TransportAddress_ipAddress));
   if(!ooUtilsIsStrEmpty(call->remoteIP))
      pIpAddressRemote = (H225TransportAddress_ipAddress*)memAlloc(pctxt, 
                                      sizeof(H225TransportAddress_ipAddress));

   if(!pIpAddressLocal || (!ooUtilsIsStrEmpty(call->remoteIP) && (!pIpAddressRemote)))
   {
      OOTRACEERR1("Error:Failed to allocate memory for Call Signalling "
                  "Addresses of ARQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pIpAddressLocal->ip.data);

   pIpAddressLocal->ip.numocts = 4;
   pIpAddressLocal->port = gH323ep.listenPort;

   if(!ooUtilsIsStrEmpty(call->remoteIP))
   {
      ooSocketConvertIpToNwAddr(call->remoteIP, pIpAddressRemote->ip.data);
      pIpAddressRemote->ip.numocts = 4;
      pIpAddressRemote->port = call->remotePort;
   }

   if(!strcmp(call->callType, "incoming"))
   {
      pAdmReq->m.destCallSignalAddressPresent = TRUE;
      pAdmReq->destCallSignalAddress.t = T_H225TransportAddress_ipAddress;
      pAdmReq->destCallSignalAddress.u.ipAddress = pIpAddressLocal;
      if(!ooUtilsIsStrEmpty(call->remoteIP))
      {
         pAdmReq->m.srcCallSignalAddressPresent = TRUE;
         pAdmReq->srcCallSignalAddress.t = T_H225TransportAddress_ipAddress;
         pAdmReq->srcCallSignalAddress.u.ipAddress = pIpAddressRemote;
      }
   }
   else {
      pAdmReq->m.srcCallSignalAddressPresent = TRUE;
      pAdmReq->srcCallSignalAddress.t = T_H225TransportAddress_ipAddress;
      pAdmReq->srcCallSignalAddress.u.ipAddress = pIpAddressLocal;
      if(!ooUtilsIsStrEmpty(call->remoteIP))
      {
         pAdmReq->m.destCallSignalAddressPresent = TRUE;
         pAdmReq->destCallSignalAddress.t = T_H225TransportAddress_ipAddress;
         pAdmReq->destCallSignalAddress.u.ipAddress = pIpAddressRemote;
      }
   }

   /* Populate seq number */
   pAdmReq->requestSeqNum = pGkClient->requestSeqNum++;
   if(!pAdmReq->requestSeqNum)
      pAdmReq->requestSeqNum = pGkClient->requestSeqNum++;

   /* Populate call type - For now only PointToPoint supported*/
   pAdmReq->callType.t = T_H225CallType_pointToPoint;
   
   /* Add call model to message*/
   pAdmReq->m.callModelPresent = 1;
   if(OO_TESTFLAG(call->flags, OO_M_GKROUTED))
      pAdmReq->callModel.t = T_H225CallModel_gatekeeperRouted;
   else
      pAdmReq->callModel.t = T_H225CallModel_direct;

   /* Populate Endpoint Identifier */
   pAdmReq->endpointIdentifier.nchars = pGkClient->endpointId.nchars;
   pAdmReq->endpointIdentifier.data = (ASN116BITCHAR*)memAlloc(pctxt,
                           sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);
   if(!pAdmReq->endpointIdentifier.data)
   {
      OOTRACEERR3("Error:Memory -  ooGkClientSendAdmissionRequest - "
                  "endpointIdentifier.data(%s, %s)\n", call->callType, 
                  call->callToken);
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memcpy((void*)pAdmReq->endpointIdentifier.data, 
          (void*)pGkClient->endpointId.data,
          sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);

   /* Get Destination And source aliases for call -  */
   if(!strcmp(call->callType, "incoming"))
   {
      if(call->ourAliases) 
         destAliases = call->ourAliases;
      else
         destAliases = gH323ep.aliases; 

      srcAliases = call->remoteAliases;
   }
   else {
      if(call->ourAliases) 
         srcAliases = call->ourAliases;
      else
         srcAliases = gH323ep.aliases; 

      destAliases = call->remoteAliases;
   }

   /* Populate destination info */
   if(destAliases)
   {
      pAdmReq->m.destinationInfoPresent = 1;
      if(OO_OK != ooPopulateAliasList(&pGkClient->msgCtxt, destAliases,
                      &pAdmReq->destinationInfo, T_H225AliasAddress_dialedDigits))
      {
         OOTRACEERR1("Error:Failed to populate destination aliases - "
                    "ARQ message\n");
         pGkClient->state = GkClientFailed;
         memReset(pctxt);
	 ast_mutex_unlock(&pGkClient->Lock);
         return OO_FAILED;
      }
   }

   /* Populate Source Info */
   if(srcAliases)
   {
      iRet = ooPopulateAliasList(&pGkClient->msgCtxt, srcAliases,
                              &pAdmReq->srcInfo, 0);
      if(OO_OK != iRet)
      {
         OOTRACEERR1("Error:Failed to populate source aliases -ARQ message\n");
         memReset(pctxt);
         pGkClient->state = GkClientFailed;
	 ast_mutex_unlock(&pGkClient->Lock);
         return OO_FAILED;
      }
   }
   
   /* Populate bandwidth*/
   pAdmReq->bandWidth = DEFAULT_BW_REQUEST;
   /* Populate call Reference */
   pAdmReq->callReferenceValue = call->callReference;
   
   /* populate conferenceID */
   memcpy((void*)&pAdmReq->conferenceID, (void*)&call->confIdentifier,
                                         sizeof(H225ConferenceIdentifier));
   /*populate answerCall */
   if(!strcmp(call->callType, "incoming"))
      pAdmReq->answerCall = TRUE;
   else
      pAdmReq->answerCall = FALSE;

   /* Populate CanMapAlias */
   pAdmReq->m.canMapAliasPresent = TRUE;
   pAdmReq->canMapAlias = FALSE;

   /* Populate call identifier */
   pAdmReq->m.callIdentifierPresent = TRUE;
   memcpy((void*)&pAdmReq->callIdentifier, (void*)&call->callIdentifier,
                                             sizeof(H225CallIdentifier));

   /* Populate Gatekeeper Id */
   if (pGkClient->gkId.nchars) {
    pAdmReq->m.gatekeeperIdentifierPresent = TRUE;
    pAdmReq->gatekeeperIdentifier.nchars = pGkClient->gkId.nchars;
    pAdmReq->gatekeeperIdentifier.data = (ASN116BITCHAR*)memAlloc(pctxt,
                                 sizeof(ASN116BITCHAR)*pGkClient->gkId.nchars);
    if(!pAdmReq->gatekeeperIdentifier.data)
    {
      OOTRACEERR1("Error:Failed to allocate memory for GKID of ARQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
    }
    memcpy((void*)pAdmReq->gatekeeperIdentifier.data, 
          (void*)pGkClient->gkId.data, 
          sizeof(ASN116BITCHAR)*pGkClient->gkId.nchars);
   }

   pAdmReq->m.willSupplyUUIEsPresent = 1;
   pAdmReq->willSupplyUUIEs = FALSE;

   /* Create RasCallAdmissionInfo */
   if(!retransmit)
   {
      pCallAdmInfo = (RasCallAdmissionInfo*)memAlloc(&pGkClient->ctxt, 
                                                sizeof(RasCallAdmissionInfo));
      if(!pCallAdmInfo)
      {
         OOTRACEERR1("Error: Failed to allocate memory for new CallAdmission"
                  " Info entry\n");
         memReset(pctxt);
         pGkClient->state = GkClientFailed;
	 ast_mutex_unlock(&pGkClient->Lock);
         return OO_FAILED;
      } 

      pCallAdmInfo->call = call;
      pCallAdmInfo->retries = 0;
      pCallAdmInfo->requestSeqNum = pAdmReq->requestSeqNum;
      dListAppend(&pGkClient->ctxt, &pGkClient->callsPendingList,pCallAdmInfo);
   }
   else{
      for(x=0; x<pGkClient->callsPendingList.count; x++)
      {
         pNode = dListFindByIndex(&pGkClient->callsPendingList, x);
         pCallAdmInfo = (RasCallAdmissionInfo*)pNode->data;
         if(pCallAdmInfo->call->callReference == call->callReference)
         {
            pCallAdmInfo->requestSeqNum = pAdmReq->requestSeqNum;
            break;
         }
      }
   }
   
   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send AdmissionRequest message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   OOTRACEINFO3("Admission Request message sent for (%s, %s)\n", 
                 call->callType, call->callToken);
   memReset(pctxt);
    
   /* Add ARQ timer */
   cbData = (ooGkClientTimerCb*) memAlloc
                               (&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
   if(!cbData)
   {
      OOTRACEERR1("Error:Failed to allocate memory for Regisration timer."
                  "\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   cbData->timerType = OO_ARQ_TIMER;
   cbData->pGkClient = pGkClient;
   cbData->pAdmInfo =  pCallAdmInfo;
   if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList, 
                  &ooGkClientARQTimerExpired, pGkClient->arqTimeout, 
                  cbData, FALSE))
   {
      OOTRACEERR1("Error:Unable to create ARQ timer.\n ");
      memFreePtr(&pGkClient->ctxt, cbData);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }    
   
   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}

/**
 * Manage incoming ACF message.
 */

int ooGkClientHandleAdmissionConfirm
   (ooGkClient *pGkClient, H225AdmissionConfirm *pAdmissionConfirm)
{
   RasCallAdmissionInfo* pCallAdmInfo=NULL;
   unsigned int x, y;
   DListNode *pNode, *pNode1=NULL;
   H225TransportAddress_ipAddress * ipAddress=NULL;
   OOTimer *pTimer = NULL;
   char ip[20];

   ast_mutex_lock(&pGkClient->Lock);

   /* Search call in pending calls list */
   for(x=0 ; x<pGkClient->callsPendingList.count; x++)
   {
      pNode = dListFindByIndex(&pGkClient->callsPendingList, x);
      pCallAdmInfo = (RasCallAdmissionInfo*) pNode->data;
      if(pCallAdmInfo->requestSeqNum == pAdmissionConfirm->requestSeqNum)
      {
         OOTRACEDBGC3("Found Pending call(%s, %s)\n", 
                      pCallAdmInfo->call->callType, 
                      pCallAdmInfo->call->callToken);

         ast_mutex_lock(&pCallAdmInfo->call->GkLock);

         /* Populate Remote IP */
         if(pAdmissionConfirm->destCallSignalAddress.t != 
                                      T_H225TransportAddress_ipAddress)
         {
            OOTRACEERR1("Error:Destination Call Signal Address provided by"
                        "Gatekeeper is not an IPv4 address\n");
            OOTRACEINFO1("Ignoring ACF, will wait for timeout and retransmit "
                         "ARQ\n");
            ast_mutex_unlock(&pCallAdmInfo->call->GkLock);
            ast_mutex_unlock(&pGkClient->Lock);
            ast_cond_signal(&pCallAdmInfo->call->gkWait);
            return OO_FAILED;
         }
         ipAddress = pAdmissionConfirm->destCallSignalAddress.u.ipAddress;
         
         sprintf(ip, "%d.%d.%d.%d", ipAddress->ip.data[0],
                                    ipAddress->ip.data[1],
                                    ipAddress->ip.data[2],
                                    ipAddress->ip.data[3]);
         if(strcmp(ip, "0.0.0.0"))
            strcpy(pCallAdmInfo->call->remoteIP, ip);
         pCallAdmInfo->call->remotePort = ipAddress->port;
         /* Update call model */
         if(pAdmissionConfirm->callModel.t == T_H225CallModel_direct)
         {
            if(OO_TESTFLAG(pCallAdmInfo->call->flags, OO_M_GKROUTED))
            {
               OOTRACEINFO3("Gatekeeper changed call model from GkRouted to "
                            "direct. (%s, %s)\n", pCallAdmInfo->call->callType,
                            pCallAdmInfo->call->callToken);
               OO_CLRFLAG(pCallAdmInfo->call->flags, OO_M_GKROUTED);
            }
         }
         
         if(pAdmissionConfirm->callModel.t == T_H225CallModel_gatekeeperRouted)
         {
            if(!OO_TESTFLAG(pCallAdmInfo->call->flags, OO_M_GKROUTED))
            {
               OOTRACEINFO3("Gatekeeper changed call model from direct to "
                            "GkRouted. (%s, %s)\n", 
                            pCallAdmInfo->call->callType,
                            pCallAdmInfo->call->callToken);
               OO_SETFLAG(pCallAdmInfo->call->flags, OO_M_GKROUTED);
            }
         }

         /* Delete ARQ timer */
         for(y=0; y<pGkClient->timerList.count; y++)
         {
            pNode1 =  dListFindByIndex(&pGkClient->timerList, y);
            pTimer = (OOTimer*)pNode1->data;
            if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_ARQ_TIMER)
            {
               if(((ooGkClientTimerCb*)pTimer->cbData)->pAdmInfo == 
                                                                 pCallAdmInfo)
               {
                  memFreePtr(&pGkClient->ctxt, pTimer->cbData);
                  ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, 
                                                                       pTimer);
                  OOTRACEDBGA1("Deleted ARQ Timer.\n");
                  break;
               }
            }
         }       
         OOTRACEINFO3("Admission Confirm message received for (%s, %s)\n", 
                       pCallAdmInfo->call->callType, 
                       pCallAdmInfo->call->callToken);

	 pCallAdmInfo->call->callState = OO_CALL_CONNECTING;

         dListRemove(&pGkClient->callsPendingList, pNode);
         dListAppend(&pGkClient->ctxt, &pGkClient->callsAdmittedList, 
                                                        pNode->data);
         memFreePtr(&pGkClient->ctxt, pNode);
         ast_mutex_unlock(&pCallAdmInfo->call->GkLock);
	 ast_mutex_unlock(&pGkClient->Lock);
	 ast_cond_signal(&pCallAdmInfo->call->gkWait);
         return OO_OK;
      }
      else
      {
         pNode = pNode->next;
      }
   }
   OOTRACEERR1("Error: Failed to process ACF as there is no corresponding "
               "pending call\n");
   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}


int ooGkClientHandleAdmissionReject
   (ooGkClient *pGkClient, H225AdmissionReject *pAdmissionReject)
{
   RasCallAdmissionInfo* pCallAdmInfo=NULL;
   unsigned int x, y;
   DListNode *pNode=NULL, *pNode1=NULL;
   OOH323CallData *call=NULL;
   OOTimer *pTimer = NULL;

   ast_mutex_lock(&pGkClient->Lock);

   /* Search call in pending calls list */
   for(x=0 ; x<pGkClient->callsPendingList.count; x++)
   {
      pNode = dListFindByIndex(&pGkClient->callsPendingList, x);
      pCallAdmInfo = (RasCallAdmissionInfo*) pNode->data;
      if(pCallAdmInfo->requestSeqNum == pAdmissionReject->requestSeqNum)
         break;
      pNode = NULL;
      pCallAdmInfo = NULL;
   }

   if(!pCallAdmInfo)
   {
      OOTRACEWARN2("Received admission reject with request number %d can not"
                   " be matched with any pending call.\n", 
                   pAdmissionReject->requestSeqNum);
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_OK;
   }
   else{
      call = pCallAdmInfo->call;
      dListRemove(&pGkClient->callsPendingList, pNode);
      memFreePtr(&pGkClient->ctxt, pCallAdmInfo);
      memFreePtr(&pGkClient->ctxt, pNode);
   }
   ast_mutex_lock(&pCallAdmInfo->call->GkLock);

   /* Delete ARQ timer */
   for(y=0; y<pGkClient->timerList.count; y++)
   {
     pNode1 =  dListFindByIndex(&pGkClient->timerList, y);
     pTimer = (OOTimer*)pNode1->data;
     if(((ooGkClientTimerCb*)pTimer->cbData)->timerType & OO_ARQ_TIMER)
     {
               if(((ooGkClientTimerCb*)pTimer->cbData)->pAdmInfo == 
                                                                 pCallAdmInfo)
               {
                  memFreePtr(&pGkClient->ctxt, pTimer->cbData);
                  ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, 
                                                                       pTimer);
                  OOTRACEDBGA1("Deleted ARQ Timer.\n");
                  break;
               }
     }
   }       
   OOTRACEINFO4("Admission Reject message received with reason code %d for "
                "(%s, %s)\n", pAdmissionReject->rejectReason.t, call->callType,
                 call->callToken);
   
   call->callState = OO_CALL_CLEARED;

   switch(pAdmissionReject->rejectReason.t)
   {
      case T_H225AdmissionRejectReason_calledPartyNotRegistered:
         call->callEndReason = OO_REASON_GK_NOCALLEDUSER;
         break;
      case T_H225AdmissionRejectReason_invalidPermission:
      case T_H225AdmissionRejectReason_requestDenied:
      case T_H225AdmissionRejectReason_undefinedReason:
         call->callEndReason = OO_REASON_GK_CLEARED;
         break;
      case T_H225AdmissionRejectReason_callerNotRegistered:
         call->callEndReason = OO_REASON_GK_NOCALLERUSER;
         break;
      case T_H225AdmissionRejectReason_exceedsCallCapacity:
      case T_H225AdmissionRejectReason_resourceUnavailable:
         call->callEndReason = OO_REASON_GK_NORESOURCES;
         break;
      case T_H225AdmissionRejectReason_noRouteToDestination:
      case T_H225AdmissionRejectReason_unallocatedNumber:
         call->callEndReason = OO_REASON_GK_UNREACHABLE;
         break;
      case T_H225AdmissionRejectReason_routeCallToGatekeeper:
      case T_H225AdmissionRejectReason_invalidEndpointIdentifier:
      case T_H225AdmissionRejectReason_securityDenial:
      case T_H225AdmissionRejectReason_qosControlNotSupported:
      case T_H225AdmissionRejectReason_incompleteAddress:
      case T_H225AdmissionRejectReason_aliasesInconsistent:
      case T_H225AdmissionRejectReason_routeCallToSCN:
      case T_H225AdmissionRejectReason_collectDestination:
      case T_H225AdmissionRejectReason_collectPIN:
      case T_H225AdmissionRejectReason_genericDataReason:
      case T_H225AdmissionRejectReason_neededFeatureNotSupported:
      case T_H225AdmissionRejectReason_securityErrors:
      case T_H225AdmissionRejectReason_securityDHmismatch:
      case T_H225AdmissionRejectReason_extElem1:
         call->callEndReason = OO_REASON_GK_CLEARED;
         break;
   }

   ast_mutex_unlock(&pCallAdmInfo->call->GkLock);
   ast_mutex_unlock(&pGkClient->Lock);
   ast_cond_signal(&pCallAdmInfo->call->gkWait);
   return OO_OK;   
}


int ooGkClientSendIRR
   (ooGkClient *pGkClient, OOH323CallData *call)
{
   int iRet = 0;
   H225RasMessage *pRasMsg=NULL;
   OOCTXT* pctxt;
   H225InfoRequestResponse *pIRR=NULL;
   H225TransportAddress_ipAddress *pIpAddressLocal =NULL, *pIpRasAddress,
				   *pLocalAddr, *pRemoteAddr;
   H225TransportAddress *pTransportAddress;
   ooAliases *srcAliases=NULL;
   H225InfoRequestResponse_perCallInfo_element *perCallInfo = NULL;
   pctxt = &pGkClient->msgCtxt;

   ast_mutex_lock(&pGkClient->Lock);

   OOTRACEDBGA3("Building Info Request Resp for call (%s, %s)\n", 
                 call->callType, call->callToken);   
   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR3("Error:Memory - ooGkClientSendIRR - "
                  "pRasMsg(%s, %s)\n", call->callType, call->callToken);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pRasMsg->t = T_H225RasMessage_infoRequestResponse;
   pIRR = (H225InfoRequestResponse*) memAlloc(pctxt, 
                                                 sizeof(H225InfoRequestResponse));
   if(!pIRR)
   {
      OOTRACEERR3("Error:Memory - ooGkClientSendIRR - "
                  "pIRR(%s, %s)\n", call->callType, call->callToken);
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memset(pIRR, 0, sizeof(H225InfoRequestResponse));
   pRasMsg->u.infoRequestResponse = pIRR;
   
   /* Populate call signalling addresses */
   pIpAddressLocal = (H225TransportAddress_ipAddress*)memAlloc(pctxt, 
                                     sizeof(H225TransportAddress_ipAddress));
   pTransportAddress = (H225TransportAddress*) memAlloc(pctxt,
                                                 sizeof(H225TransportAddress));
   if(!pIpAddressLocal || !pTransportAddress)
   {
      OOTRACEERR1("Error:Failed to allocate memory for Call Signalling "
                  "Addresses of IRR message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pIpAddressLocal->ip.data);

   pIpAddressLocal->ip.numocts = 4;
   pIpAddressLocal->port = gH323ep.listenPort;

   pTransportAddress->t = T_H225TransportAddress_ipAddress;
   pTransportAddress->u.ipAddress = pIpAddressLocal;

   dListInit(&pIRR->callSignalAddress);
   dListAppend(pctxt, &pIRR->callSignalAddress,
                                       (void*)pTransportAddress);

   /* Populate seq number */
   pIRR->requestSeqNum = pGkClient->requestSeqNum++;
   if(!pIRR->requestSeqNum)
      pIRR->requestSeqNum = pGkClient->requestSeqNum++;

   pIpRasAddress = (H225TransportAddress_ipAddress*)memAlloc(pctxt, 
                                     sizeof(H225TransportAddress_ipAddress));
   if(!pIpRasAddress)
   {
      OOTRACEERR1("Error: Memory allocation for Ras Address of IRR message "
                  "failed\n");
      memReset(&pGkClient->msgCtxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   pIpRasAddress->ip.numocts = 4;
   pIpRasAddress->port = pGkClient->localRASPort;
   ooSocketConvertIpToNwAddr(pGkClient->localRASIP, pIpRasAddress->ip.data);

   pIRR->rasAddress.u.ipAddress = pIpRasAddress;
   pIRR->rasAddress.t=T_H225TransportAddress_ipAddress; /* IPv4 address */

   /* Pose as gateway or terminal as per config */
   if(gH323ep.isGateway)
      pIRR->endpointType.m.gatewayPresent = TRUE;
   else
      pIRR->endpointType.m.terminalPresent = TRUE;

   pIRR->endpointType.m.nonStandardDataPresent=FALSE;
   pIRR->endpointType.m.vendorPresent=TRUE;
   ooGkClientFillVendor(pGkClient, &pIRR->endpointType.vendor);

   /* Populate Endpoint Identifier */
   pIRR->endpointIdentifier.nchars = pGkClient->endpointId.nchars;
   pIRR->endpointIdentifier.data = (ASN116BITCHAR*)memAlloc(pctxt,
                           sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);
   if(!pIRR->endpointIdentifier.data)
   {
      OOTRACEERR3("Error:Memory -  ooGkClientSendIRR - "
                  "endpointIdentifier.data(%s, %s)\n", call->callType,
                  call->callToken);
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memcpy((void*)pIRR->endpointIdentifier.data,
          (void*)pGkClient->endpointId.data,
          sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);


   /* Populate call aliases */
   if(call->ourAliases) 
      srcAliases = call->ourAliases;
   else
      srcAliases = gH323ep.aliases; 

   /* Populate Source Info */
   if(srcAliases)
   {
      iRet = ooPopulateAliasList(&pGkClient->msgCtxt, srcAliases,
                             &pIRR->endpointAlias, T_H225AliasAddress_h323_ID);
      if(OO_OK != iRet)
      {
         OOTRACEERR1("Error:Failed to populate source aliases -IRR message\n");
         memReset(pctxt);
         pGkClient->state = GkClientFailed;
	 ast_mutex_unlock(&pGkClient->Lock);
         return OO_FAILED;
      }
   }
   pIRR->m.endpointAliasPresent = TRUE;

   /* Populate need response & unsolicited */
   pIRR->needResponse = FALSE;
   pIRR->m.needResponsePresent = TRUE;
   pIRR->unsolicited = TRUE;
   pIRR->m.unsolicitedPresent = TRUE;
   
   /* Populate perCallInfo */

   pIRR->m.perCallInfoPresent = TRUE;

   perCallInfo = 
    (H225InfoRequestResponse_perCallInfo_element *)memAlloc(pctxt,
     sizeof(H225InfoRequestResponse_perCallInfo_element));
   memset(perCallInfo, 0, sizeof(H225InfoRequestResponse_perCallInfo_element));

   if(!perCallInfo)
   {
      OOTRACEERR3("Error:Memory -  ooGkClientSendIRR - "
                  "perCallInfo for (%s, %s)\n", call->callType,
                  call->callToken);
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   perCallInfo->m.originatorPresent = TRUE;
   perCallInfo->originator = (!strcmp(call->callType, "incoming")) ? FALSE : TRUE;

   pLocalAddr = (H225TransportAddress_ipAddress*)memAlloc(pctxt,
                                     sizeof(H225TransportAddress_ipAddress));
   pRemoteAddr = (H225TransportAddress_ipAddress*) memAlloc(pctxt,
                                     sizeof(H225TransportAddress_ipAddress));
   if(!pLocalAddr || !pRemoteAddr)
   {
      OOTRACEERR1("Error:Failed to allocate memory for Call Signalling "
                  "Addresses of IRR message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pLocalAddr->ip.numocts = 4;
   ooSocketConvertIpToNwAddr(call->localIP, pLocalAddr->ip.data);
   pLocalAddr->port = (call->pH225Channel->port) ? call->pH225Channel->port : gH323ep.listenPort;

   pRemoteAddr->ip.numocts = 4;
   ooSocketConvertIpToNwAddr(call->remoteIP, pRemoteAddr->ip.data);
   pRemoteAddr->port = call->remotePort;

   perCallInfo->callSignaling.m.sendAddressPresent = TRUE;
   perCallInfo->callSignaling.sendAddress.t = T_H225TransportAddress_ipAddress; 
   perCallInfo->callSignaling.m.recvAddressPresent = TRUE;
   perCallInfo->callSignaling.recvAddress.t = T_H225TransportAddress_ipAddress; 

   if (!strcmp(call->callType, "incoming")) {
// terminator
     perCallInfo->callSignaling.sendAddress.u.ipAddress = pRemoteAddr;
     perCallInfo->callSignaling.recvAddress.u.ipAddress = pLocalAddr;
   } else {
// originator
     perCallInfo->callSignaling.sendAddress.u.ipAddress = pLocalAddr;
     perCallInfo->callSignaling.recvAddress.u.ipAddress = pRemoteAddr;
   }

   /* Populate call Reference */
   perCallInfo->callReferenceValue = call->callReference;
   /* populate conferenceID */
   memcpy((void*)&perCallInfo->conferenceID, (void*)&call->confIdentifier,
                                         sizeof(H225ConferenceIdentifier));
   /* Populate call identifier */
   perCallInfo->m.callIdentifierPresent = TRUE;
   memcpy((void*)&perCallInfo->callIdentifier, (void*)&call->callIdentifier,
                                             sizeof(H225CallIdentifier));
   /* Populate call type & call model */
   perCallInfo->callType.t = T_H225CallType_pointToPoint;
   /* Add call model to message*/
   if(OO_TESTFLAG(call->flags, OO_M_GKROUTED))
      perCallInfo->callModel.t = T_H225CallModel_gatekeeperRouted;
   else
      perCallInfo->callModel.t = T_H225CallModel_direct;

   /* Populate usage info */
   if (call->alertingTime) {
     perCallInfo->usageInformation.m.alertingTimePresent = TRUE;
     perCallInfo->usageInformation.alertingTime = call->alertingTime;
   }
   if (call->connectTime) {
    perCallInfo->usageInformation.m.connectTimePresent = TRUE;
    perCallInfo->usageInformation.connectTime = call->connectTime;
   }
   perCallInfo->usageInformation.m.endTimePresent = FALSE;
   perCallInfo->m.usageInformationPresent = TRUE;
   
   dListInit(&pIRR->perCallInfo);
   dListAppend(pctxt, &pIRR->perCallInfo,
                                       (void*)perCallInfo);

   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send IRR message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   OOTRACEINFO3("IRR message sent for (%s, %s)\n", 
                 call->callType, call->callToken);
   memReset(pctxt);
    
   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}

/**
 * This function is invoked to request call disengage to gatekeeper. 
 * 
 * @param   szCallToken    Call token.     
 *
 * @return  Completion status - 0 on success, -1 on failure
 */

int ooGkClientSendDisengageRequest(ooGkClient *pGkClient, OOH323CallData *call)
{
   int iRet = 0;   
   unsigned int x;
   H225RasMessage *pRasMsg=NULL;
   OOCTXT *pctxt = NULL;
   DListNode *pNode = NULL;
   H225DisengageRequest * pDRQ = NULL;
   RasCallAdmissionInfo* pCallAdmInfo=NULL;
   pctxt = &pGkClient->msgCtxt;

   ast_mutex_lock(&pGkClient->Lock);

   OOTRACEINFO3("Sending disengage Request for  call. (%s, %s)\n",
                 call->callType, call->callToken);

   pRasMsg = (H225RasMessage*)memAlloc(pctxt, sizeof(H225RasMessage));
   if(!pRasMsg)
   {
      OOTRACEERR1("Error: Memory allocation for DRQ RAS message failed\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   pRasMsg->t = T_H225RasMessage_disengageRequest;
   pDRQ = (H225DisengageRequest*) memAlloc(pctxt, 
                                               sizeof(H225DisengageRequest));
   if(!pDRQ)
   {
      OOTRACEERR1("Error: Failed to allocate memory for DRQ message\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

   memset(pDRQ, 0, sizeof(H225DisengageRequest));
   pRasMsg->u.disengageRequest = pDRQ;
   
   pDRQ->requestSeqNum = pGkClient->requestSeqNum++;
   if(!pDRQ->requestSeqNum )
      pDRQ->requestSeqNum = pGkClient->requestSeqNum++;
   
   
   pDRQ->endpointIdentifier.nchars = pGkClient->endpointId.nchars;
   pDRQ->endpointIdentifier.data = (ASN116BITCHAR*)memAlloc(pctxt,
                           sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);
   if(!pDRQ->endpointIdentifier.data)
   {
      OOTRACEERR1("Error: Failed to allocate memory for EndPoint Id in DRQ "
                  "message.\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   memcpy((void*)pDRQ->endpointIdentifier.data, 
                 (void*)pGkClient->endpointId.data, 
                 sizeof(ASN116BITCHAR)*pGkClient->endpointId.nchars);

   memcpy((void*)&pDRQ->conferenceID, (void*)&call->confIdentifier,
                                         sizeof(H225ConferenceIdentifier));

   pDRQ->callReferenceValue = call->callReference;
   
   pDRQ->disengageReason.t = T_H225DisengageReason_normalDrop;

   pDRQ->m.answeredCallPresent = 1;
   if(!strcmp(call->callType, "incoming"))
      pDRQ->answeredCall = 1;
   else
      pDRQ->answeredCall = 0;

   pDRQ->m.callIdentifierPresent = 1;
   memcpy((void*)&pDRQ->callIdentifier, (void*)&call->callIdentifier,
                                             sizeof(H225CallIdentifier));
   if (pGkClient->gkId.nchars) {
    pDRQ->m.gatekeeperIdentifierPresent = 1;
    pDRQ->gatekeeperIdentifier.nchars = pGkClient->gkId.nchars;
    pDRQ->gatekeeperIdentifier.data = (ASN116BITCHAR*)memAlloc
                       (pctxt, pGkClient->gkId.nchars*sizeof(ASN116BITCHAR));
    if(!pDRQ->gatekeeperIdentifier.data)
    {
      OOTRACEERR1("Error:Failed to allocate memory for GKId in DRQ.\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
    }
    memcpy(pDRQ->gatekeeperIdentifier.data, pGkClient->gkId.data, 
                                pGkClient->gkId.nchars*sizeof(ASN116BITCHAR));
   }

   pDRQ->m.terminationCausePresent = 1;
   pDRQ->terminationCause.t = T_H225CallTerminationCause_releaseCompleteCauseIE;
   pDRQ->terminationCause.u.releaseCompleteCauseIE = 
      (H225CallTerminationCause_releaseCompleteCauseIE*)memAlloc(pctxt,
      sizeof(H225CallTerminationCause_releaseCompleteCauseIE));
   if(!pDRQ->terminationCause.u.releaseCompleteCauseIE)
   {
      OOTRACEERR1("Error: Failed to allocate memory for cause ie in DRQ.\n");
      memReset(pctxt);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   pDRQ->terminationCause.u.releaseCompleteCauseIE->numocts = 
                                                         strlen("Call Ended");
   strcpy((char *)pDRQ->terminationCause.u.releaseCompleteCauseIE->data, "Call Ended");

   /* populate usage info */

   /* Populate usage info */
   if (call->alertingTime) {
     pDRQ->usageInformation.m.alertingTimePresent = TRUE;
     pDRQ->usageInformation.alertingTime = call->alertingTime;
   }
   if (call->connectTime) {
    pDRQ->usageInformation.m.connectTimePresent = TRUE;
    pDRQ->usageInformation.connectTime = call->connectTime;
   }
   pDRQ->usageInformation.m.endTimePresent = TRUE;
   if (call->endTime)
    pDRQ->usageInformation.endTime = call->endTime;
   else
    pDRQ->usageInformation.endTime = time(NULL);
   pDRQ->m.usageInformationPresent = TRUE;

   iRet = ooGkClientSendMsg(pGkClient, pRasMsg);
   if(iRet != OO_OK)
   {
      OOTRACEERR1("Error: Failed to send DRQ message\n");
      pGkClient->state = GkClientFailed;
   }
   


   /* Search call in admitted calls list */
   for(x=0 ; x<pGkClient->callsAdmittedList.count ; x++)
   {
      pNode = (DListNode*)dListFindByIndex(&pGkClient->callsAdmittedList, x);
      pCallAdmInfo = (RasCallAdmissionInfo*) pNode->data;
      if(pCallAdmInfo->call->callReference == call->callReference)
      {
         dListRemove( &pGkClient->callsAdmittedList, pNode);
         memFreePtr(&pGkClient->ctxt, pNode->data);
         memFreePtr(&pGkClient->ctxt, pNode);
         break;
      }
   }
   ast_mutex_unlock(&pGkClient->Lock);
   return iRet;
}     

int ooGkClientHandleDisengageConfirm
   (ooGkClient *pGkClient, H225DisengageConfirm *pDCF)
{
   OOTRACEINFO1("Received disengage confirm\n");
   return OO_OK;
}

int ooGkClientRRQTimerExpired(void*pdata)
{
   int ret=0;
   ooGkClientTimerCb *cbData = (ooGkClientTimerCb*)pdata;
   ooGkClient *pGkClient = cbData->pGkClient;
   OOTRACEDBGA1("Gatekeeper client RRQ timer expired.\n");
   
   if(pGkClient->rrqRetries < OO_MAX_RRQ_RETRIES)
   {
      ret = ooGkClientSendRRQ(pGkClient, 0);      
      if(ret != OO_OK)
      {
         OOTRACEERR1("Error:Failed to send RRQ message\n");
         
         return OO_FAILED;
      }
      pGkClient->rrqRetries++;
      memFreePtr(&pGkClient->ctxt, cbData);
      return OO_OK;
   }
   memFreePtr(&pGkClient->ctxt, cbData);
   OOTRACEERR1("Error:Failed to register with gatekeeper\n");
   pGkClient->state = GkClientUnregistered;


/* Create timer to re-register after default timeout */
/* network failure is one of cases here */

   ast_mutex_lock(&pGkClient->Lock);

   cbData = (ooGkClientTimerCb*) memAlloc
				(&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
   if(!cbData)
   {
      OOTRACEERR1("Error:Failed to allocate memory to RRQ timer callback\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }


   cbData->timerType = OO_RRQ_TIMER;
   cbData->pGkClient = pGkClient;
   if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList,
                     &ooGkClientRRQTimerExpired, pGkClient->regTimeout,
                     cbData, FALSE))
   {
      OOTRACEERR1("Error:Unable to create GRQ timer.\n ");
      memFreePtr(&pGkClient->ctxt, cbData);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }

/* clear rrq count for re-register after regTimeout */
   pGkClient->rrqRetries = 0;

   ast_mutex_unlock(&pGkClient->Lock);

   return OO_FAILED;
}

int ooGkClientGRQTimerExpired(void* pdata)
{
   int ret=0;
   ooGkClientTimerCb *cbData = (ooGkClientTimerCb*)pdata;
   ooGkClient *pGkClient = cbData->pGkClient;

   OOTRACEDBGA1("Gatekeeper client GRQ timer expired.\n");

   memFreePtr(&pGkClient->ctxt, cbData);   

   if(pGkClient->grqRetries < OO_MAX_GRQ_RETRIES)
   {
      ret = ooGkClientSendGRQ(pGkClient);      
      if(ret != OO_OK)
      {
         OOTRACEERR1("Error:Failed to send GRQ message\n");
         pGkClient->state = GkClientFailed;
         return OO_FAILED;
      }
      pGkClient->grqRetries++;
      return OO_OK;
   }

   OOTRACEERR1("Error:Gatekeeper could not be found\n");
   pGkClient->state = GkClientUnregistered;
/* setup timer to re-send grq after timeout */

   ast_mutex_lock(&pGkClient->Lock);
   cbData = (ooGkClientTimerCb*) memAlloc
                               (&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
   if(!cbData)
   {
      OOTRACEERR1("Error:Failed to allocate memory to GRQ timer callback\n");
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
   cbData->timerType = OO_GRQ_TIMER;
   cbData->pGkClient = pGkClient;
   if(!ooTimerCreate(&pGkClient->ctxt, &pGkClient->timerList,
                     &ooGkClientGRQTimerExpired, pGkClient->grqTimeout,
                     cbData, FALSE))
   {
      OOTRACEERR1("Error:Unable to create GRQ timer.\n ");
      memFreePtr(&pGkClient->ctxt, cbData);
      pGkClient->state = GkClientFailed;
      ast_mutex_unlock(&pGkClient->Lock);
      return OO_FAILED;
   }
 
/* clear grq counter */

   pGkClient->grqRetries = 0;
   ast_mutex_unlock(&pGkClient->Lock);

   return OO_FAILED;
}
   
int ooGkClientREGTimerExpired(void *pdata)
{
   int ret=0;
   ooGkClientTimerCb *cbData = (ooGkClientTimerCb*)pdata;
   ooGkClient *pGkClient = cbData->pGkClient;
   OOTRACEDBGA1("Gatekeeper client additive registration timer expired\n");
   memFreePtr(&pGkClient->ctxt, cbData);   
   ret = ooGkClientSendRRQ(pGkClient, TRUE);      
   if(ret != OO_OK)
   {
      OOTRACEERR1("Error:Failed to send Additive RRQ message\n");
      pGkClient->state = GkClientFailed;
      return OO_FAILED;
   }
   return OO_OK;
}

int ooGkClientARQTimerExpired(void* pdata)
{
   int ret=0;
   ooGkClientTimerCb *cbData = (ooGkClientTimerCb*)pdata;
   ooGkClient *pGkClient = cbData->pGkClient;
   RasCallAdmissionInfo *pAdmInfo = cbData->pAdmInfo;

   OOTRACEDBGA1("Gatekeeper client ARQ timer expired.\n");
   memFreePtr(&pGkClient->ctxt, cbData);   

   if(!pAdmInfo)
    return OO_OK;

   if(pAdmInfo->retries < OO_MAX_ARQ_RETRIES)
   {
      ret = ooGkClientSendAdmissionRequest(pGkClient, pAdmInfo->call, TRUE);      
      if(ret != OO_OK)
      {
         OOTRACEERR1("Error:Failed to send ARQ message\n");
         return OO_FAILED;
      }
      pAdmInfo->retries++;
      return OO_OK;
   }

   OOTRACEERR1("Error:Gatekeeper not responding to ARQ\n");
   pGkClient->state = GkClientGkErr;
   return OO_FAILED;
}

int ooGkClientCleanCall(ooGkClient *pGkClient, OOH323CallData *call)
{
   unsigned int x=0;
   DListNode *pNode=NULL;
   OOTimer *pTimer;
   ooGkClientTimerCb *cbData=NULL;
   RasCallAdmissionInfo *pAdmInfo = NULL;

   ast_mutex_lock(&pGkClient->Lock);


   for(x=0; x<pGkClient->callsAdmittedList.count; x++)
   {
      pNode = dListFindByIndex(&pGkClient->callsAdmittedList, x);
      pAdmInfo = (RasCallAdmissionInfo*)pNode->data;
      if(pAdmInfo->call->callReference == call->callReference)
      {
         dListRemove(&pGkClient->callsAdmittedList, pNode);
         memFreePtr(&pGkClient->ctxt, pAdmInfo);
         memFreePtr(&pGkClient->ctxt, pNode);
	 break;
      }
   }


   for(x=0; x<pGkClient->timerList.count; x++)
   {
      pNode = dListFindByIndex(&pGkClient->timerList, x);
      pTimer = (OOTimer*)pNode->data;
      cbData = (ooGkClientTimerCb*)pTimer->cbData;
      if(cbData->timerType & OO_ARQ_TIMER &&
         cbData->pAdmInfo->call->callReference == call->callReference)
      {
         memFreePtr(&pGkClient->ctxt, pTimer->cbData);
         ooTimerDelete(&pGkClient->ctxt, &pGkClient->timerList, pTimer);
         break;
      }
   }

   for(x=0; x<pGkClient->callsPendingList.count; x++)
   {
      pNode = dListFindByIndex(&pGkClient->callsPendingList, x);
      pAdmInfo = (RasCallAdmissionInfo*)pNode->data;
      if(pAdmInfo->call->callReference == call->callReference)
      {
         dListRemove(&pGkClient->callsPendingList, pNode);
         memFreePtr(&pGkClient->ctxt, pAdmInfo);
         memFreePtr(&pGkClient->ctxt, pNode);
	 break;
      }
   }

   ast_mutex_unlock(&pGkClient->Lock);
   return OO_OK;
}

/*
 * TODO: In case of GkErr, if GkMode is DiscoverGatekeeper,
 *       need to cleanup gkrouted calls, and discover another
 *       gatekeeper.
 * Note: This function returns OO_FAILED, when we can not recover from
 *       the failure.
 */
int ooGkClientHandleClientOrGkFailure(ooGkClient *pGkClient)
{
   if(pGkClient->state == GkClientFailed)
   {
      OOTRACEERR1("Error: Internal Failure in GkClient. Closing "
                  "GkClient\n");
      ooGkClientDestroy();
      return OO_FAILED;
   }
   else if(pGkClient->state == GkClientGkErr) {
      OOTRACEERR1("Error: Gatekeeper error. Either Gk not responding or "
                  "Gk sending invalid messages\n");
      if(pGkClient->gkMode == RasUseSpecificGatekeeper)
      {
         OOTRACEERR1("Error: Gatekeeper error detected. Closing GkClient as "
                     "Gk mode is UseSpecifcGatekeeper\n");
         ooGkClientDestroy();
         return OO_FAILED;
      }
      else{
         OOTRACEERR1("Error: Gatekeeper error detected. Closing GkClient. NEED"
                    " to implement recovery by rediscovering another gk\n");
         ooGkClientDestroy();
         return OO_FAILED;
      }
   }

   return OO_FAILED;
}

/**
 * TODO: This fuction might not work properly in case of additive registrations
 * For example we registrered 10 aliases and gatekeeper accepted 8 of them.
 * Now we want to register another two new aliases(not out of those first 10).
 * Gk responds with RCF with empty terminalAlias field thus indicating both 
 * the aliases were accepted. If this function is called, it will even mark
 * the earlier two unregistered aliases as registered. We will have to
 * maintain a separete list of aliases being sent in RRQ for this.
 */
int ooGkClientUpdateRegisteredAliases
   (ooGkClient *pGkClient, H225_SeqOfH225AliasAddress *pAddresses, 
    OOBOOL registered)
{
   int i=0, j, k;
   DListNode* pNode=NULL;
   ooAliases *pAlias=NULL;
   H225AliasAddress *pAliasAddress=NULL;
   H225TransportAddress *pTransportAddrss=NULL;
   char value[MAXFILENAME];
   OOBOOL bAdd = FALSE;

   if(!pAddresses)
   {
     /* All aliases registered/unregistsred */
      pAlias = gH323ep.aliases;
      
      while(pAlias)
      {
         pAlias->registered = registered?TRUE:FALSE;
         pAlias = pAlias->next;
      }
      return OO_OK;
   }

   /* Mark aliases as registered/unregistered*/
   if(pAddresses->count<=0)
      return OO_FAILED;

   for(i=0; i<(int)pAddresses->count; i++)
   {
      pNode = dListFindByIndex (pAddresses, i);
      if(!pNode)
      {
         OOTRACEERR1("Error:Invalid alias list passed to "
                     "ooGkClientUpdateRegisteredAliases\n");
         continue;
      }
      pAliasAddress = (H225AliasAddress*)pNode->data;
      
      if(!pAliasAddress){
         OOTRACEERR1("Error:Invalid alias list passed to "
                     "ooGkClientUpdateRegisteredAliases\n");
         continue;
      }

      switch(pAliasAddress->t)
      {
      case T_H225AliasAddress_dialedDigits:
         pAlias = ooH323GetAliasFromList(gH323ep.aliases, 
                                          T_H225AliasAddress_dialedDigits, 
                                        (char*)pAliasAddress->u.dialedDigits);
         if(pAlias)
         {
            pAlias->registered = registered?TRUE:FALSE;
         }
         else{
            bAdd = registered?TRUE:FALSE;
         }
         break;
      case T_H225AliasAddress_h323_ID:
         for(j=0, k=0; j<(int)pAliasAddress->u.h323_ID.nchars && (k<MAXFILENAME-1); j++)
         {
            if(pAliasAddress->u.h323_ID.data[j] < 256)
            {
               value[k++] = (char) pAliasAddress->u.h323_ID.data[j];
            }
         }
         value[k] = '\0';
         pAlias = ooH323GetAliasFromList(gH323ep.aliases, 
                                         T_H225AliasAddress_h323_ID, 
                                          value);
         if(pAlias)
         {
            pAlias->registered = registered?TRUE:FALSE;
         }
         else{
            bAdd = registered?TRUE:FALSE;
         }
         break;
      case T_H225AliasAddress_url_ID:
         pAlias = ooH323GetAliasFromList(gH323ep.aliases, 
                                         T_H225AliasAddress_url_ID, 
                                       (char*)pAliasAddress->u.url_ID);
         if(pAlias)
         {
            pAlias->registered = registered?TRUE:FALSE;
         }
         else{
            bAdd = registered?TRUE:FALSE;
         }
         break;
      case T_H225AliasAddress_transportID:
         pTransportAddrss = pAliasAddress->u.transportID;
         if(pTransportAddrss->t != T_H225TransportAddress_ipAddress)
         {
            OOTRACEERR1("Error:Alias transportID not IP address\n");
            break;
         }
         
         sprintf(value, "%d.%d.%d.%d:%d", 
                          pTransportAddrss->u.ipAddress->ip.data[0],
                          pTransportAddrss->u.ipAddress->ip.data[1],
                          pTransportAddrss->u.ipAddress->ip.data[2],
                          pTransportAddrss->u.ipAddress->ip.data[3],
                          pTransportAddrss->u.ipAddress->port);

         pAlias = ooH323GetAliasFromList(gH323ep.aliases, 
                                         T_H225AliasAddress_transportID, 
                                         value);
         if(pAlias)
         {
            pAlias->registered = registered?TRUE:FALSE;
         }
         else{
            bAdd = registered?TRUE:FALSE;
         }
         break;
      case T_H225AliasAddress_email_ID:
         pAlias = ooH323GetAliasFromList(gH323ep.aliases, 
                                         T_H225AliasAddress_email_ID, 
                                       (char*) pAliasAddress->u.email_ID);
         if(pAlias)
         {
            pAlias->registered = registered?TRUE:FALSE;
         }
         else{
            bAdd = registered?TRUE:FALSE;
         }
         break;
      default:
         OOTRACEERR1("Error:Unhandled alias type found in registered "
                     "aliases\n");
      }
      if(bAdd)
      {
         pAlias = ooH323AddAliasToList(&gH323ep.aliases, 
                                           &gH323ep.ctxt, pAliasAddress);
         if(pAlias){
            pAlias->registered = registered?TRUE:FALSE;
         }
         else{
            OOTRACEERR2("Warning:Could not add registered alias of "
                        "type %d to list.\n", pAliasAddress->t);
         }
         bAdd = FALSE;
      }
      pAlias = NULL;
   }
   return OO_OK;
}
