/****************************************************************************
 *
 *	$Archive:   S:/STURGEON/SRC/Q931/VCS/q931.c_v  $
 *
 *  INTEL Corporation Prorietary Information
 *
 *  This listing is supplied under the terms of a license agreement
 *  with INTEL Corporation and may not be copied nor disclosed except
 *  in accordance with the terms of that agreement.
 *
 *	Copyright (c) 1993-1996 Intel Corporation.
 *
 *	$Revision:   1.122.1.0  $
 *	$Date:   20 Jun 1997 14:12:46  $
 *	$Author:   MANDREWS  $
 *
 *	BCL's revision info:
 *	Revision:   1.99  
 *	Date:   19 Nov 1996 14:54:02  
 *	Author:   rodellx  
 *
 *	Deliverable:
 *
 *	Abstract:
 *		
 *
 *	Notes:
 *
 ***************************************************************************/
// [ ] Add facility ie to FACILITY MSG.
// [ ] Read Q931 Appendix D.

#pragma warning ( disable : 4057 4100 4115 4201 4214 4514 )

#ifdef __cplusplus
extern "C" {
#endif

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <string.h>
#include <stdio.h>

#include <rpc.h>

#include "apierror.h"
#include "isrg.h"
#include "incommon.h"
#include "linkapi.h"

#include "common.h"
#include "q931.h"
#include "utils.h"
#include "hlisten.h"
#include "hcall.h"
#include "q931pdu.h"
#include "provider.h"

#if (defined(_DEBUG) && defined(PCS_COMPLIANCE))
#include "interop.h"
#include "q931plog.h"
LPInteropLogger Q931Logger;
#endif

#define RECEIVE_BUFFER_SIZE 0x2000
#define HANGUP_TIMEOUT              1000        // 1 second
#define CANCEL_LISTEN_TIMEOUT       5000        // 5 seconds

// variable needed to support ISR debug facility.
#if (ISRDEBUGINFO >= 1)
WORD ghISRInst = 0;
#endif

#ifdef UNICODE_TRACE
// We include this header to fix problems with macro expansion when Unicode is turned on.
#include "unifix.h"
#endif

#define _Unicode(x) L ## x
#define Unicode(x) _Unicode(x)

// global data used by WinSock.
static BOOL bQ931Initialized = FALSE;

static Q931_RECEIVE_PDU_CALLBACK gReceivePDUHookProc = NULL;

static struct
{
    DWORD TempID;
    CC_CONFERENCEID ConferenceID;
    CRITICAL_SECTION Lock;
} ConferenceIDSource;

//====================================================================================
//
// PRIVATE FUNCTIONS
//
//====================================================================================

//====================================================================================
//====================================================================================
void _FreeSetupASN(Q931_SETUP_ASN *pSetupASN)
{
	ASSERT(pSetupASN != NULL);

	// Cleanup any dynamically allocated fields within SetupASN
	if (pSetupASN->NonStandardData.sData.pOctetString)
	{
		Free(pSetupASN->NonStandardData.sData.pOctetString);
		pSetupASN->NonStandardData.sData.pOctetString = NULL;
	}
	if (pSetupASN->VendorInfo.pProductNumber)
	{
		Free(pSetupASN->VendorInfo.pProductNumber);
		pSetupASN->VendorInfo.pProductNumber = NULL;
	}
	if (pSetupASN->VendorInfo.pVersionNumber)
	{
		Free(pSetupASN->VendorInfo.pVersionNumber);
		pSetupASN->VendorInfo.pVersionNumber = NULL;
	}
	Q931FreeAliasNames(pSetupASN->pCallerAliasList);
	pSetupASN->pCallerAliasList = NULL;
	Q931FreeAliasNames(pSetupASN->pCalleeAliasList);
	pSetupASN->pCalleeAliasList = NULL;
	Q931FreeAliasNames(pSetupASN->pExtraAliasList);
	pSetupASN->pExtraAliasList = NULL;
	Q931FreeAliasItem(pSetupASN->pExtensionAliasItem);
	pSetupASN->pExtensionAliasItem = NULL;
}


void _FreeReleaseCompleteASN(Q931_RELEASE_COMPLETE_ASN *pReleaseCompleteASN)
{
	ASSERT(pReleaseCompleteASN != NULL);

	// Cleanup any dynamically allocated fields within SetupASN
	if (pReleaseCompleteASN->NonStandardData.sData.pOctetString)
	{
		Free(pReleaseCompleteASN->NonStandardData.sData.pOctetString);
		pReleaseCompleteASN->NonStandardData.sData.pOctetString = NULL;
	}
}


void _FreeFacilityASN(Q931_FACILITY_ASN *pFacilityASN)
{
	ASSERT(pFacilityASN != NULL);

	// Cleanup any dynamically allocated fields within SetupASN
	if (pFacilityASN->NonStandardData.sData.pOctetString)
	{
		Free(pFacilityASN->NonStandardData.sData.pOctetString);
		pFacilityASN->NonStandardData.sData.pOctetString = NULL;
	}
}


void _FreeProceedingASN(Q931_CALL_PROCEEDING_ASN *pProceedingASN)
{
	ASSERT(pProceedingASN != NULL);

	// Cleanup any dynamically allocated fields within SetupASN
	if (pProceedingASN->NonStandardData.sData.pOctetString)
	{
		Free(pProceedingASN->NonStandardData.sData.pOctetString);
		pProceedingASN->NonStandardData.sData.pOctetString = NULL;
	}
}


void _FreeAlertingASN(Q931_ALERTING_ASN *pAlertingASN)
{
	ASSERT(pAlertingASN != NULL);

	// Cleanup any dynamically allocated fields within SetupASN
	if (pAlertingASN->NonStandardData.sData.pOctetString)
	{
		Free(pAlertingASN->NonStandardData.sData.pOctetString);
		pAlertingASN->NonStandardData.sData.pOctetString = NULL;
	}
}


void _FreeConnectASN(Q931_CONNECT_ASN *pConnectASN)
{
	ASSERT(pConnectASN != NULL);

	// Cleanup any dynamically allocated fields within SetupASN
	if (pConnectASN->NonStandardData.sData.pOctetString)
	{
		Free(pConnectASN->NonStandardData.sData.pOctetString);
		pConnectASN->NonStandardData.sData.pOctetString = NULL;
	}
	if (pConnectASN->VendorInfo.pProductNumber)
    {
        Free(pConnectASN->VendorInfo.pProductNumber);
        pConnectASN->VendorInfo.pProductNumber = NULL;
    }
    if (pConnectASN->VendorInfo.pVersionNumber)
    {
        Free(pConnectASN->VendorInfo.pVersionNumber);
        pConnectASN->VendorInfo.pVersionNumber = NULL;
    }
}


void
_ConferenceIDNew(
    CC_CONFERENCEID *pConferenceID)
{
    UUID id;
    int iresult;
    
    EnterCriticalSection(&(ConferenceIDSource.Lock));

    memset(ConferenceIDSource.ConferenceID.buffer, 0,
        sizeof(ConferenceIDSource.ConferenceID.buffer));
    iresult = UuidCreate(&id);
  
    if ((iresult == RPC_S_OK) || (iresult ==RPC_S_UUID_LOCAL_ONLY))
    {
        memcpy(ConferenceIDSource.ConferenceID.buffer, &id,
            min(sizeof(ConferenceIDSource.ConferenceID.buffer), sizeof(UUID)));
    }
    else
        ASSERT(0);
    
    memcpy(pConferenceID->buffer, ConferenceIDSource.ConferenceID.buffer,
        sizeof(pConferenceID->buffer));

    LeaveCriticalSection(&(ConferenceIDSource.Lock));
    return;
}

//====================================================================================
// This function is used internally whenever a function needs to send a PDU.
// Note that before a datalinkSendRequest() is done, the call object is unlocked
// and then subsequently locked after returning.  This is necessary to prevent deadlock
// in MT apps.  Further, it is the responsibility of calling functions to revalidate
// the call object before using it.
//====================================================================================
CS_STATUS 
Q931SendMessage(
    P_CALL_OBJECT       pCallObject,
    BYTE*               CodedPtrPDU, 
    DWORD               CodedLengthPDU,
    BOOL                bOkToUnlock)
{
    HQ931CALL           hQ931Call;
    DWORD               dwPhysicalId;
    HRESULT             result;

    ASSERT(pCallObject != NULL);
    ASSERT(CodedPtrPDU != NULL);
    ASSERT(CodedLengthPDU != 0);

    hQ931Call    = pCallObject->hQ931Call;
    dwPhysicalId = pCallObject->dwPhysicalId;

    // send the message.
    if (pCallObject->bConnected)
    {
        // Unlock the call object before we call down to Link Layer (if caller said it was ok)
        if(bOkToUnlock)
            CallObjectUnlock(pCallObject);

#if (defined(_DEBUG) && defined(PCS_COMPLIANCE))
        InteropOutput(Q931Logger, (BYTE FAR*)CodedPtrPDU, CodedLengthPDU, Q931LOG_SENT_PDU);
#endif

        result = datalinkSendRequest(dwPhysicalId, CodedPtrPDU, CodedLengthPDU);

        // Now attempt to lock the object again.  Note: higher level funcs must
        // be sure to call CallObjectValidate before assuming they have a valid lock
        if (bOkToUnlock && ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)))
        {
            Q931DBG((DBGERROR, "CallObjectLock() returned error (object not found)."));
        }

        // Note: if we can't get the lock, perhaps we should pass back a specific return code
        // that higher layers can check for??? For now, they should call CallObjectValidate()
        // before assuming the call object is still good.
        if (FAILED(result))
        {
            Q931DBG((DBGERROR, "datalinkSendRequest() failed"));
            Free(CodedPtrPDU);
        }
        return result;
    }

    Q931DBG((DBGWARNING, "Q931SendMessage: message not sent because bConnected is FALSE"));
    Free(CodedPtrPDU);
    return CS_OK;
}


//====================================================================================
//====================================================================================
CS_STATUS
Q931RingingInternal(P_CALL_OBJECT pCallObject, WORD wCRV)
{
    CC_ENDPOINTTYPE EndpointType;
    DWORD CodedLengthASN;
    BYTE *CodedPtrASN;
    BINARY_STRING UserUserData;
    DWORD CodedLengthPDU;
    BYTE *CodedPtrPDU;
    HRESULT result = CS_OK;
    int nError = 0;
	HQ931CALL hQ931Call = pCallObject->hQ931Call;

    if (pCallObject->VendorInfoPresent)
        EndpointType.pVendorInfo = &pCallObject->VendorInfo;
    else
        EndpointType.pVendorInfo = NULL;
    EndpointType.bIsTerminal = pCallObject->bIsTerminal;
    EndpointType.bIsGateway = pCallObject->bIsGateway;

    result = Q931AlertingEncodeASN(
        NULL, /* pNonStandardData */
        NULL, /* h245Addr */
        &EndpointType,
        &pCallObject->World,
        &CodedPtrASN,
        &CodedLengthASN);

    if (result != CS_OK || CodedLengthASN == 0 || CodedPtrASN == NULL)
    {
        Q931DBG((DBGERROR, "Q931AlertingEncodeASN() failed, nothing to send."));
        if (CodedPtrASN != NULL)
        {
            Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
        }
        return (result != CS_OK) ? result : CS_INTERNAL_ERROR;
    }

    UserUserData.length = (WORD)CodedLengthASN;
    UserUserData.ptr = CodedPtrASN;

    result = Q931AlertingEncodePDU(wCRV, &UserUserData, &CodedPtrPDU,
        &CodedLengthPDU);

    if (CodedPtrASN != NULL)
    {
        Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
    }

    if ((result != CS_OK) || (CodedLengthPDU == 0) ||
            (CodedPtrPDU == NULL))
    {
        Q931DBG((DBGERROR, "Q931AlertingEncodePDU() failed, nothing to send."));
        if (CodedPtrPDU != NULL)
        {
            Free(CodedPtrPDU);
        }
        if (result != CS_OK)
        {
            return result;
        }
        return CS_INTERNAL_ERROR;
    }
    else
    {
        result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE);
        if(CallObjectValidate(hQ931Call) != CS_OK)
            return(CS_INTERNAL_ERROR);

    }
    return result;
}


//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallSetup(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    Q931_SETUP_ASN *pSetupASN)
{
    DWORD		result;
	HQ931CALL	hQ931Call;
	HRESULT		Status;

    // if the callstate is anything other than null, ignore...
//    if (pCallObject->bCallState != CALLSTATE_NULL)
//    {
//        return CS_OK;
//    }

	hQ931Call = pCallObject->hQ931Call;

    if (pMessage->CallReference & 0x8000)
    {
        // the message came from the callee, so this should be ignored???
    }
    pMessage->CallReference &= ~(0x8000);    // strip off the high bit.
    pCallObject->wCRV = pMessage->CallReference;

    pCallObject->wGoal = pSetupASN->wGoal;
    pCallObject->bCallerIsMC = pSetupASN->bCallerIsMC;
    pCallObject->wCallType = pSetupASN->wCallType;
    pCallObject->ConferenceID = pSetupASN->ConferenceID;

    pCallObject->bCallState = CALLSTATE_PRESENT;

    {
        CSS_CALL_INCOMING EventData;
        WCHAR szUnicodeDisplay[CC_MAX_DISPLAY_LENGTH + 1];
        WCHAR szUnicodeCalledPartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1];
        WCHAR szUnicodeCallingPartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1];

        EventData.wGoal = pCallObject->wGoal;
        EventData.wCallType = pCallObject->wCallType;
        EventData.bCallerIsMC = pCallObject->bCallerIsMC;
        EventData.ConferenceID = pCallObject->ConferenceID;

        EventData.pSourceAddr = NULL;
        if (pSetupASN->SourceAddrPresent)
        {
            EventData.pSourceAddr = &(pSetupASN->SourceAddr);
        }

        EventData.pCallerAddr = NULL;
        if (pSetupASN->CallerAddrPresent)
        {
            EventData.pCallerAddr = &(pSetupASN->CallerAddr);
        }

        EventData.pCalleeDestAddr = NULL;
        if (pSetupASN->CalleeDestAddrPresent)
        {
            EventData.pCalleeDestAddr = &(pSetupASN->CalleeDestAddr);
        }

        EventData.pLocalAddr = NULL;
        if (pSetupASN->CalleeAddrPresent)
        {
            EventData.pLocalAddr = &(pSetupASN->CalleeAddr);
        }

        if (!(pSetupASN->NonStandardDataPresent) ||
                (pSetupASN->NonStandardData.sData.wOctetStringLength == 0) ||
                (pSetupASN->NonStandardData.sData.pOctetString == NULL))
        {
            EventData.pNonStandardData = NULL;
        }
        else
        {
            EventData.pNonStandardData = &(pSetupASN->NonStandardData);
        }

        EventData.pCallerAliasList = pSetupASN->pCallerAliasList;
        EventData.pCalleeAliasList = pSetupASN->pCalleeAliasList;
        EventData.pExtraAliasList = pSetupASN->pExtraAliasList;
        EventData.pExtensionAliasItem = pSetupASN->pExtensionAliasItem;

        EventData.pszDisplay = NULL;
        if (pMessage->Display.Present && pMessage->Display.Contents)
        {
            MultiByteToWideChar(CP_ACP, 0, (const char *)pMessage->Display.Contents, -1,
                szUnicodeDisplay, sizeof(szUnicodeDisplay) / sizeof(szUnicodeDisplay[0]));
            EventData.pszDisplay = szUnicodeDisplay;
        }

        EventData.pszCalledPartyNumber = NULL;
        if (pMessage->CalledPartyNumber.Present && pMessage->CalledPartyNumber.PartyNumberLength)
        {
            

            MultiByteToWideChar(CP_ACP, 0, (const char *)pMessage->CalledPartyNumber.PartyNumbers, -1,
                szUnicodeCalledPartyNumber, sizeof(szUnicodeCalledPartyNumber) / sizeof(szUnicodeCalledPartyNumber[0]));
            EventData.pszCalledPartyNumber = szUnicodeCalledPartyNumber;
        }

        //nikhilb:change for CPR
        if (pMessage->CallingPartyNumber.Present && (pMessage->CallingPartyNumber.Length > 1) )
        {
            if( (EventData.pCallerAliasList == NULL) || 
                (EventData.pCallerAliasList->wCount == 0) || 
                (EventData.pCallerAliasList->pItems == NULL) )
            {
                // Always skip 1st byte in the contents.
                // Skip the 2nd byte if its not an e.164 char (could be the screening indicator byte)

                BYTE* pstrTemp = pMessage->CallingPartyNumber.Contents +
                    ((pMessage->CallingPartyNumber.Contents[1] & 0x80)? 2 : 1);

                MultiByteToWideChar(CP_ACP, 0, (const char *)(pstrTemp), -1,
                    szUnicodeCallingPartyNumber, 
                    sizeof(szUnicodeCallingPartyNumber) / sizeof(szUnicodeCallingPartyNumber[0]));
            
                if( EventData.pCallerAliasList == NULL )
                {
                    EventData.pCallerAliasList = (PCC_ALIASNAMES)malloc( sizeof(CC_ALIASNAMES) );
                }
                
                if( EventData.pCallerAliasList != NULL )
                {
                    EventData.pCallerAliasList->wCount = 1;
                
                    EventData.pCallerAliasList->pItems = (PCC_ALIASITEM)malloc( sizeof(CC_ALIASITEM) );
                    if( EventData.pCallerAliasList->pItems == NULL )
                    {
                        free( EventData.pCallerAliasList );
                        EventData.pCallerAliasList = NULL;
                    }
                    else
                    {
                        ZeroMemory( EventData.pCallerAliasList->pItems, sizeof(CC_ALIASITEM) );
                        EventData.pCallerAliasList->pItems[0].wType = CC_ALIAS_H323_PHONE;
                        EventData.pCallerAliasList->pItems[0].wDataLength = (WORD)wcslen( szUnicodeCallingPartyNumber );
                
                        EventData.pCallerAliasList->pItems[0].pData = 
                            (WCHAR*) malloc( (EventData.pCallerAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) );
                
                        if( EventData.pCallerAliasList->pItems[0].pData == NULL )
                        {
                            free( EventData.pCallerAliasList->pItems );
                            free( EventData.pCallerAliasList );
                            EventData.pCallerAliasList = NULL;
                        }
                        else
                        {
                            CopyMemory( EventData.pCallerAliasList->pItems[0].pData,
                                szUnicodeCallingPartyNumber, 
                                (EventData.pCallerAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) );
                        }
                    }
                }
            }
        }


        //nikhilb:change for CPR
        if (pMessage->CalledPartyNumber.Present && (pMessage->CalledPartyNumber.PartyNumberLength > 0) )
        {
            if( (EventData.pCalleeAliasList == NULL) || 
                (EventData.pCalleeAliasList->wCount == 0) || 
                (EventData.pCalleeAliasList->pItems == NULL) )
            {
                MultiByteToWideChar(CP_ACP, 0, (const char *)(pMessage->CalledPartyNumber.PartyNumbers), -1,
                    szUnicodeCalledPartyNumber, sizeof(szUnicodeCalledPartyNumber) / sizeof(szUnicodeCalledPartyNumber[0]));
            
                if( EventData.pCalleeAliasList == NULL )
                {
                    EventData.pCalleeAliasList = (PCC_ALIASNAMES)malloc( sizeof(CC_ALIASNAMES) );
                }
                
                if( EventData.pCalleeAliasList != NULL )
                {
                    EventData.pCalleeAliasList->wCount = 1;
                
                    EventData.pCalleeAliasList->pItems = (PCC_ALIASITEM)malloc( sizeof(CC_ALIASITEM) );
                    if( EventData.pCalleeAliasList->pItems == NULL )
                    {
                        free( EventData.pCalleeAliasList );
                        EventData.pCalleeAliasList = NULL;
                    }
                    else
                    {
                        ZeroMemory( EventData.pCalleeAliasList->pItems, sizeof(CC_ALIASITEM) );
                        EventData.pCalleeAliasList->pItems[0].wType = CC_ALIAS_H323_PHONE;
                        EventData.pCalleeAliasList->pItems[0].wDataLength = (WORD)wcslen( szUnicodeCalledPartyNumber );
                
                        EventData.pCalleeAliasList->pItems[0].pData = 
                            (WCHAR*) malloc( (EventData.pCalleeAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) );
                
                        if( EventData.pCalleeAliasList->pItems[0].pData == NULL )
                        {
                            free( EventData.pCalleeAliasList->pItems );
                            free( EventData.pCalleeAliasList );
                            EventData.pCalleeAliasList = NULL;
                        }
                        else
                        {
                            CopyMemory( EventData.pCalleeAliasList->pItems[0].pData,
                                szUnicodeCalledPartyNumber, 
                                (EventData.pCalleeAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) );
                        }
                    }
                }
            }
        }

        EventData.pSourceEndpointType = &(pSetupASN->EndpointType);

        EventData.wCallReference = pMessage->CallReference;

        result = pCallObject->Callback((BYTE)Q931_CALL_INCOMING,
            pCallObject->hQ931Call, pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);

    }

	Status = CallObjectValidate(hQ931Call);
	if (Status != CS_OK)
		return Status;

    if (result == 0)
    {
        WORD wCRV = (WORD)(pMessage->CallReference | 0x8000);
        HRESULT Status;

        Status = Q931RingingInternal(pCallObject, wCRV);
        if (Status != CS_OK)
        {
            return Status;
        }
        pCallObject->bCallState = CALLSTATE_RECEIVED;
    }
    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931Ringing(
    HQ931CALL hQ931Call,
    WORD *pwCRV)
{
    P_CALL_OBJECT pCallObject = NULL;
    CS_STATUS Status;
    WORD wCRV;

    if (bQ931Initialized == FALSE)
    {
    	ASSERT(FALSE);
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Entering Q931Ringing()..."));

    // need parameter checking...
    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error (object not found)."));
        return CS_BAD_PARAM;
    }

    if (pwCRV != NULL)
    {
        wCRV = *pwCRV;
    }
    else
    {
        wCRV = pCallObject->wCRV;
    }

    Status = Q931RingingInternal(pCallObject, wCRV);
    if (Status != CS_OK)
    {
        return Status;
    }

    pCallObject->bCallState = CALLSTATE_RECEIVED;
    Status = CallObjectUnlock(pCallObject);

    return Status;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallProceeding(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    Q931_CALL_PROCEEDING_ASN *pProceedingASN)
{
    pCallObject->bCallState = CALLSTATE_OUTGOING;

    Q931StopTimer(pCallObject, Q931_TIMER_303);

    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallAlerting(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    Q931_ALERTING_ASN *pAlertingASN)
{
    DWORD result;

    pCallObject->bCallState = CALLSTATE_DELIVERED;

    if (pAlertingASN != NULL)
    {
        // we could pass h245addr, userinfo, and conferenceid
        // if desired later...
        // (this would be passed in the pAlertingASN field)
    }

    Q931StopTimer(pCallObject, Q931_TIMER_303);
    Q931StartTimer(pCallObject, Q931_TIMER_301);

    result = pCallObject->Callback((BYTE)Q931_CALL_RINGING,
        pCallObject->hQ931Call, pCallObject->dwListenToken,
        pCallObject->dwUserToken, NULL);

    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallConnect(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    Q931_CONNECT_ASN *pConnectASN)
{
    DWORD result;

    if ((pMessage->CallReference & 0x8000) == 0)
    {
        // the message came from the caller, so this should be ignored???
    }
    pMessage->CallReference &= ~(0x8000);    // strip off the high bit.

    pCallObject->ConferenceID = pConnectASN->ConferenceID;

    pCallObject->bCallState = CALLSTATE_ACTIVE;

    {
        CSS_CALL_ACCEPTED EventData;
        WCHAR szUnicodeDisplay[CC_MAX_DISPLAY_LENGTH + 1];

        // populate the event data struct.

        EventData.ConferenceID = pCallObject->ConferenceID;

        if (pCallObject->PeerCallAddrPresent)
        {
            EventData.pCalleeAddr = &(pCallObject->PeerCallAddr);
        }
        else
        {
            EventData.pCalleeAddr = NULL;
        }
        EventData.pLocalAddr = &(pCallObject->LocalAddr);

        EventData.pH245Addr = NULL;
        if (pConnectASN->h245AddrPresent)
        {
            EventData.pH245Addr = &(pConnectASN->h245Addr);
        }

        if (!(pConnectASN->NonStandardDataPresent) ||
                (pConnectASN->NonStandardData.sData.wOctetStringLength == 0) ||
                (pConnectASN->NonStandardData.sData.pOctetString == NULL))
        {
            EventData.pNonStandardData = NULL;
        }
        else
        {
            EventData.pNonStandardData = &(pConnectASN->NonStandardData);
        }

        EventData.pszDisplay = NULL;
        if (pMessage->Display.Present && pMessage->Display.Contents)
        {
            MultiByteToWideChar(CP_ACP, 0, (const char *)pMessage->Display.Contents, -1,
                szUnicodeDisplay, sizeof(szUnicodeDisplay) / sizeof(szUnicodeDisplay[0]));
            EventData.pszDisplay = szUnicodeDisplay;
        }

        EventData.pDestinationEndpointType = &(pConnectASN->EndpointType);

        EventData.wCallReference = pMessage->CallReference;

        Q931StopTimer(pCallObject, Q931_TIMER_303);
        Q931StopTimer(pCallObject, Q931_TIMER_301);

        result = pCallObject->Callback((BYTE)Q931_CALL_ACCEPTED,
            pCallObject->hQ931Call, pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);
    }

    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallReleaseComplete(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    Q931_RELEASE_COMPLETE_ASN *pReleaseCompleteASN)
{
    DWORD result;
    BYTE bCause = 0;

    if (pMessage && pMessage->Cause.Present &&
            (pMessage->Cause.Length >= 3))
    {
        bCause = (BYTE)(pMessage->Cause.Contents[2] & (~CAUSE_EXT_BIT));
    }

    Q931StopTimer(pCallObject, Q931_TIMER_303);
    Q931StopTimer(pCallObject, Q931_TIMER_301);

    // if this is the callee, or the call has been connected already,
    // then this message should be treated as hangup (not reject).
    if (!(pCallObject->fIsCaller) ||
            (pCallObject->bCallState == CALLSTATE_ACTIVE) ||
            (bCause == CAUSE_VALUE_NORMAL_CLEAR))
    {
        CSS_CALL_REMOTE_HANGUP EventData;

        EventData.bReason = CC_REJECT_NORMAL_CALL_CLEARING;
        pCallObject->bCallState = CALLSTATE_NULL;

        result = pCallObject->Callback((BYTE)Q931_CALL_REMOTE_HANGUP,
            pCallObject->hQ931Call, pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);
    }
    else
    {
        CSS_CALL_REJECTED EventData;

        pCallObject->bCallState = CALLSTATE_NULL;

        // populate the event data struct.
        switch (bCause)
        {
            case CAUSE_VALUE_NORMAL_CLEAR:
                EventData.bRejectReason = CC_REJECT_NORMAL_CALL_CLEARING;
                break;
            case CAUSE_VALUE_USER_BUSY:
                EventData.bRejectReason = CC_REJECT_USER_BUSY;
                break;
            case CAUSE_VALUE_NO_ANSWER:
                EventData.bRejectReason = CC_REJECT_NO_ANSWER;
                break;
            case CAUSE_VALUE_NOT_IMPLEMENTED:
                EventData.bRejectReason = CC_REJECT_NOT_IMPLEMENTED;
                break;
            case CAUSE_VALUE_INVALID_CRV:
                EventData.bRejectReason = CC_REJECT_INVALID_IE_CONTENTS;
                break;
            case CAUSE_VALUE_IE_MISSING:
                EventData.bRejectReason = CC_REJECT_MANDATORY_IE_MISSING;
                break;
            case CAUSE_VALUE_IE_CONTENTS:
                EventData.bRejectReason = CC_REJECT_INVALID_IE_CONTENTS;
                break;
            case CAUSE_VALUE_TIMER_EXPIRED:
                EventData.bRejectReason = CC_REJECT_TIMER_EXPIRED;
                break;
            default:
                EventData.bRejectReason = pReleaseCompleteASN->bReason;
                break;
        }

        EventData.ConferenceID = pCallObject->ConferenceID;

        EventData.pAlternateAddr = NULL;

        if (!(pReleaseCompleteASN->NonStandardDataPresent) ||
                (pReleaseCompleteASN->NonStandardData.sData.wOctetStringLength == 0) ||
                (pReleaseCompleteASN->NonStandardData.sData.pOctetString == NULL))
        {
            EventData.pNonStandardData = NULL;
        }
        else
        {
            EventData.pNonStandardData = &(pReleaseCompleteASN->NonStandardData);
        }

        result = pCallObject->Callback((BYTE)Q931_CALL_REJECTED,
            pCallObject->hQ931Call, pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);
    }

    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallFacility(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    Q931_FACILITY_ASN *pFacilityASN)
{
    DWORD result;

    // if this is the callee, or the call has been connected already,
    // then this message should be treated as hangup (not reject).
    if (!(pCallObject->fIsCaller) ||
            (pCallObject->bCallState == CALLSTATE_ACTIVE))
    {
        CSS_CALL_REMOTE_HANGUP EventData;

        EventData.bReason = pFacilityASN->bReason;
        pCallObject->bCallState = CALLSTATE_NULL;

        result = pCallObject->Callback((BYTE)Q931_CALL_REMOTE_HANGUP,
            pCallObject->hQ931Call, pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);
    }
    else
    {
        CSS_CALL_REJECTED EventData;

        pCallObject->bCallState = CALLSTATE_NULL;

        // populate the event data struct.
        EventData.bRejectReason = pFacilityASN->bReason;

        EventData.ConferenceID = pFacilityASN->ConferenceIDPresent ?
            pFacilityASN->ConferenceID : pCallObject->ConferenceID;

        EventData.pAlternateAddr = &(pFacilityASN->AlternativeAddr);

        if (!(pFacilityASN->NonStandardDataPresent) ||
                (pFacilityASN->NonStandardData.sData.wOctetStringLength == 0) ||
                (pFacilityASN->NonStandardData.sData.pOctetString == NULL))
        {
            EventData.pNonStandardData = NULL;
        }
        else
        {
            EventData.pNonStandardData = &(pFacilityASN->NonStandardData);
        }

        result = pCallObject->Callback((BYTE)Q931_CALL_REJECTED,
            pCallObject->hQ931Call, pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);
    }

    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931SendReleaseCompleteMessage(
    P_CALL_OBJECT pCallObject,
    BYTE bRejectReason,
    PCC_CONFERENCEID pConferenceID,
    PCC_ADDR pAlternateAddr,
    PCC_NONSTANDARDDATA pNonStandardData)
{
    CS_STATUS result = CS_OK;
    HQ931CALL hQ931Call = pCallObject->hQ931Call;

    // since this call is going away, mark the call object for deletion so any other 
    // threads attempting to use this object will fail to get a lock on it.
    CallObjectMarkForDelete(hQ931Call);

    if((bRejectReason == CC_REJECT_ROUTE_TO_GATEKEEPER) ||
            (bRejectReason == CC_REJECT_CALL_FORWARDED) ||
            (bRejectReason == CC_REJECT_ROUTE_TO_MC))
    {
        // send the FACILITY message to the peer to reject the call.
        DWORD CodedLengthASN;
        BYTE *CodedPtrASN;
        HRESULT ResultASN = CS_OK;
        CC_ADDR AltAddr;

        MakeBinaryADDR(pAlternateAddr, &AltAddr);

        ResultASN = Q931FacilityEncodeASN(pNonStandardData,
            (pAlternateAddr ? &AltAddr : NULL),
            bRejectReason, pConferenceID, NULL, &pCallObject->World,
            &CodedPtrASN, &CodedLengthASN);
        if ((ResultASN != CS_OK) || (CodedLengthASN == 0) ||
                (CodedPtrASN == NULL))
        {
            Q931DBG((DBGERROR, "Q931FacilityEncodeASN() failed, nothing to send."));
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            result = CS_INTERNAL_ERROR;
        }
        else
        {
            DWORD CodedLengthPDU;
            BYTE *CodedPtrPDU;
            BINARY_STRING UserUserData;
            HRESULT ResultEncode = CS_OK;
            WORD wCRV;
            if (pCallObject->fIsCaller)
            {
                wCRV = (WORD)(pCallObject->wCRV & 0x7FFF);
            }
            else
            {
                wCRV = (WORD)(pCallObject->wCRV | 0x8000);
            }

            UserUserData.length = (WORD)CodedLengthASN;
            UserUserData.ptr = CodedPtrASN;

            ResultEncode = Q931FacilityEncodePDU(wCRV,
                &UserUserData, &CodedPtrPDU, &CodedLengthPDU);
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) ||
                    (CodedPtrPDU == NULL))
            {
                Q931DBG((DBGERROR, "Q931FacilityEncodePDU() failed, nothing to send."));
                if (CodedPtrPDU != NULL)
                {
                    Free(CodedPtrPDU);
                }
                result = CS_INTERNAL_ERROR;
            }
            else
            {
                result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, FALSE);
            }
        }
    }
    else
    {
        // send the RELEASE COMPLETE message to the peer to reject call.
        DWORD CodedLengthASN;
        BYTE *CodedPtrASN;
        HRESULT ResultASN = CS_OK;
        BYTE bReasonUU = bRejectReason;
        BYTE *pbReasonUU = &bReasonUU;

        switch (bReasonUU)
        {
            case CC_REJECT_NO_BANDWIDTH:
            case CC_REJECT_GATEKEEPER_RESOURCES:
            case CC_REJECT_UNREACHABLE_DESTINATION:
            case CC_REJECT_DESTINATION_REJECTION:
            case CC_REJECT_INVALID_REVISION:
            case CC_REJECT_NO_PERMISSION:
            case CC_REJECT_UNREACHABLE_GATEKEEPER:
            case CC_REJECT_GATEWAY_RESOURCES:
            case CC_REJECT_BAD_FORMAT_ADDRESS:
            case CC_REJECT_ADAPTIVE_BUSY:
            case CC_REJECT_IN_CONF:
            case CC_REJECT_CALL_DEFLECTION:
            case CC_REJECT_USER_BUSY:
                break;
            default:
                pbReasonUU = NULL;
                break;
        }

        ResultASN = Q931ReleaseCompleteEncodeASN(pNonStandardData,
            pConferenceID, pbReasonUU, &pCallObject->World,
            &CodedPtrASN, &CodedLengthASN);
        if ((ResultASN != CS_OK) || (CodedLengthASN == 0) ||
                (CodedPtrASN == NULL))
        {
            Q931DBG((DBGERROR, "Q931ReleaseCompleteEncodeASN() failed, nothing to send."));
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            result = CS_INTERNAL_ERROR;
        }
        else
        {
            DWORD CodedLengthPDU;
            BYTE *CodedPtrPDU;
            BINARY_STRING UserUserData;
            HRESULT ResultEncode = CS_OK;
            BYTE bCause = 0;
            BYTE *pbCause = &bCause;
            WORD wCRV;

            if (pCallObject->fIsCaller)
            {
                wCRV = (WORD)(pCallObject->wCRV & 0x7FFF);
            }
            else
            {
                wCRV = (WORD)(pCallObject->wCRV | 0x8000);
            }

            UserUserData.length = (WORD)CodedLengthASN;
            UserUserData.ptr = CodedPtrASN;

            switch (bRejectReason)
            {
                case CC_REJECT_NORMAL_CALL_CLEARING:
                    bCause = CAUSE_VALUE_NORMAL_CLEAR;
                    break;
                case CC_REJECT_USER_BUSY:
                    bCause = CAUSE_VALUE_USER_BUSY;
                    break;
                case CC_REJECT_NO_ANSWER:
                    bCause = CAUSE_VALUE_NO_ANSWER;
                    break;
                case CC_REJECT_NOT_IMPLEMENTED:
                    bCause = CAUSE_VALUE_NOT_IMPLEMENTED;
                    break;
                case CC_REJECT_MANDATORY_IE_MISSING:
                    bCause = CAUSE_VALUE_IE_MISSING;
                    break;
                case CC_REJECT_INVALID_IE_CONTENTS:
                    bCause = CAUSE_VALUE_IE_CONTENTS;
                    break;
                case CC_REJECT_TIMER_EXPIRED:
                    bCause = CAUSE_VALUE_TIMER_EXPIRED;
                    break;
                default:
                    pbCause = NULL;
                    break;
            }

            ResultEncode = Q931ReleaseCompleteEncodePDU(wCRV,
                pbCause, &UserUserData,
                &CodedPtrPDU, &CodedLengthPDU);
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) ||
                    (CodedPtrPDU == NULL))
            {
                Q931DBG((DBGERROR, "Q931ReleaseCompleteEncodePDU() failed, nothing to send."));
                if (CodedPtrPDU != NULL)
                {
                    Free(CodedPtrPDU);
                }
                result = CS_INTERNAL_ERROR;
            }
            else
            {
                result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, FALSE);
            }
        }
    }

    pCallObject->bCallState = CALLSTATE_NULL;

    if (result != CS_OK)
    {
        return result;
    }
    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931SendStatusMessage(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage,
    BYTE bCause)
{
    CS_STATUS result = CS_OK;

    DWORD CodedLengthPDU;
    BYTE *CodedPtrPDU;
    HRESULT EncodePDU = CS_OK;
	int nError = 0;
    HQ931CALL hQ931Call = pCallObject->hQ931Call;
    WORD wCRV;
    if (pCallObject->fIsCaller)
    {
        wCRV = (WORD)(pCallObject->wCRV & 0x7FFF);
    }
	else
	{
        wCRV = (WORD)(pCallObject->wCRV | 0x8000);
	}

    EncodePDU = Q931StatusEncodePDU(wCRV, NULL, bCause,
        pCallObject->bCallState, &CodedPtrPDU, &CodedLengthPDU);
    if ((EncodePDU != CS_OK) || (CodedLengthPDU == 0) ||
            (CodedPtrPDU == NULL))
    {
        Q931DBG((DBGERROR, "Q931StatusEncodePDU() failed, nothing to send."));
        if (CodedPtrPDU != NULL)
        {
            Free(CodedPtrPDU);
        }
        result = CS_INTERNAL_ERROR;
    }
    else
    {
        result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE);
        if(CallObjectValidate(hQ931Call) != CS_OK)
            return(CS_INTERNAL_ERROR);
    }
    return(result);
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931SendProceedingMessage(
    HQ931CALL hQ931Call,
    WORD wCallReference,
    PCC_ENDPOINTTYPE pDestinationEndpointType,
    PCC_NONSTANDARDDATA pNonStandardData)
{
    CS_STATUS result = CS_OK;

    DWORD CodedLengthASN;
    BYTE *CodedPtrASN;
    HRESULT ResultASN = CS_OK;
    DWORD dwPhysicalId = INVALID_PHYS_ID;
    P_CALL_OBJECT pCallObject = NULL;


    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error"));
        return CS_SUBSYSTEM_FAILURE;
    }
    dwPhysicalId = pCallObject->dwPhysicalId;

    // first build the ASN portion of the message (user to user part)
    ResultASN = Q931ProceedingEncodeASN(
        pNonStandardData,
        NULL,                          // No H245 address.
        pDestinationEndpointType,      // EndpointType information.
                &pCallObject->World,
        &CodedPtrASN,
        &CodedLengthASN);

    if ((ResultASN != CS_OK) || (CodedLengthASN == 0) ||
            (CodedPtrASN == NULL))
    {
        Q931DBG((DBGERROR, "Q931ProceedingEncodeASN() failed, nothing to send."));

        if (CodedPtrASN != NULL)
        {
            Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
        }
        result = CS_INTERNAL_ERROR;
    }
    else
    {
        // now build the rest of the message
        DWORD CodedLengthPDU;
        BYTE *CodedPtrPDU;
        BINARY_STRING UserUserData;
        HRESULT ResultEncode = CS_OK;
        WORD wCRV = (WORD)(wCallReference | 0x8000);

        UserUserData.length = (WORD)CodedLengthASN;
        UserUserData.ptr = CodedPtrASN;

        ResultEncode = Q931ProceedingEncodePDU(wCRV,
            &UserUserData, &CodedPtrPDU, &CodedLengthPDU);
        if (CodedPtrASN != NULL)
        {
            Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
        }
        if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) ||
                (CodedPtrPDU == NULL))
        {
            Q931DBG((DBGERROR, "Q931ProceedingEncodePDU() failed, nothing to send."));
            if (CodedPtrPDU != NULL)
            {
                Free(CodedPtrPDU);
            }
            result = CS_INTERNAL_ERROR;
        }
        else
        {
            result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE);

						if (CallObjectValidate(hQ931Call) != CS_OK)
							return(CS_INTERNAL_ERROR);
        }
    }
    CallObjectUnlock(pCallObject);
    return(result);
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931SendPDU(HQ931CALL hQ931Call, BYTE* CodedPtrPDU, DWORD CodedLengthPDU)
{
    CS_STATUS result = CS_OK;
    HRESULT TempResult;
    P_CALL_OBJECT pCallObject = NULL;

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) ||
            (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error"));
        return CS_SUBSYSTEM_FAILURE;
    }


    TempResult = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE);

    if (CallObjectValidate(hQ931Call) != CS_OK)
        return(CS_INTERNAL_ERROR);

    if(FAILED(TempResult))
    {
        CSS_CALL_FAILED EventData;
        EventData.error = TempResult;
        if ((pCallObject->bCallState == CALLSTATE_ACTIVE) &&
                (pCallObject->bResolved))
        {
            pCallObject->Callback(Q931_CALL_CONNECTION_CLOSED,
                pCallObject->hQ931Call, pCallObject->dwListenToken,
                pCallObject->dwUserToken, NULL);
        }
        else
        {
            pCallObject->Callback(Q931_CALL_FAILED,
                pCallObject->hQ931Call, pCallObject->dwListenToken,
                pCallObject->dwUserToken, &EventData);
        }

        if (CallObjectValidate(hQ931Call) == CS_OK)
        {
           DWORD dwId = pCallObject->dwPhysicalId;
           if ((pCallObject->bCallState != CALLSTATE_ACTIVE) ||
                   (!pCallObject->bResolved))
           {
               CallObjectDestroy(pCallObject);
               pCallObject = NULL;
           }
           linkLayerShutdown(dwId);
           if (pCallObject)
           {
               pCallObject->bConnected = FALSE;
           }
        }
        return TempResult;
    }
	CallObjectUnlock(pCallObject);
    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931OnCallStatusEnquiry(
    P_CALL_OBJECT pCallObject,
    Q931MESSAGE *pMessage)
{
    CS_STATUS SendStatus;

    SendStatus = Q931SendStatusMessage(pCallObject, pMessage,
        CAUSE_VALUE_ENQUIRY_RESPONSE);

    return SendStatus;
}

//====================================================================================
//====================================================================================
void
Q931SendComplete(DWORD instance, HRESULT msg, PBYTE buf, DWORD length)
{
    HQ931CALL hQ931Call = (HQ931CALL)instance;
    P_CALL_OBJECT pCallObject = NULL;

    Q931DBG((DBGTRACE, "Entering Q931SendComplete()..."));

    if (buf != NULL)
    {
        Free(buf);
    }

    if (FAILED(msg))
    {
        // shut down link layer; report failure to client
        CSS_CALL_FAILED EventData;

        Q931DBG((DBGERROR, "error in datalinkSendRequest()"));

        if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
        {
            Q931DBG((DBGERROR, "CallObjectLock() returned error"));
            return;
        }

        EventData.error = msg;
        if ((pCallObject->bCallState == CALLSTATE_ACTIVE) &&
                (pCallObject->bResolved))
        {
            pCallObject->Callback(Q931_CALL_CONNECTION_CLOSED,
                pCallObject->hQ931Call, pCallObject->dwListenToken,
                pCallObject->dwUserToken, NULL);
        }
        else
        {
            pCallObject->Callback(Q931_CALL_FAILED,
                pCallObject->hQ931Call, pCallObject->dwListenToken,
                pCallObject->dwUserToken, &EventData);
        }

        if (CallObjectValidate(hQ931Call) == CS_OK)
        {
             DWORD dwId = pCallObject->dwPhysicalId;
             if ((pCallObject->bCallState != CALLSTATE_ACTIVE) ||
                     (!pCallObject->bResolved))
             {
                 CallObjectDestroy(pCallObject);
                 pCallObject = NULL;
             }
             linkLayerShutdown(dwId);
             if (pCallObject)
             {
                 pCallObject->bConnected = FALSE;
             }
        }
        return;
    }
    return;
}

//====================================================================================
//====================================================================================
static DWORD
PostReceiveBuffer(DWORD dwPhysicalId, BYTE *buf)
{
    if (buf == NULL)
    {
        buf = Malloc(RECEIVE_BUFFER_SIZE);
    }
    return datalinkReceiveRequest(dwPhysicalId, buf, RECEIVE_BUFFER_SIZE);
}

//====================================================================================
//====================================================================================
void
OnReceiveCallback(DWORD instance, HRESULT message, Q931MESSAGE *pMessage, BYTE *buf, DWORD nbytes)
{
    HQ931CALL hQ931Call = (HQ931CALL)instance;
    P_CALL_OBJECT pCallObject = NULL;
    DWORD dwPhysicalId;
    
    Q931DBG((DBGTRACE, "Entering ReceiveCallback()..."));

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        if (buf)
        {
            Free(buf);
        }
        Q931DBG((DBGTRACE, "Call Object no longer available: 0x%08lx", hQ931Call));
        return;
    }

    if (message == LINK_RECV_DATA)
    {
        HRESULT Result = CS_OK;

        if ((buf == NULL) || (nbytes == 0))
        {
            Q931DBG((DBGERROR, "Empty buffer received as data."));
            CallObjectUnlock(pCallObject);
            return;
        }

        // This block is the Q931 call re-connect implementation:
        // if the object related to the incoming message is not yet resolved...
        if (pCallObject->bResolved == FALSE)
        {
            // try to resolve the object.
            HQ931CALL hFoundCallObject;
            P_CALL_OBJECT pFoundCallObject = NULL;

            // If found another object with matching CRV/Addr...
            if (CallObjectFind(&hFoundCallObject, pCallObject->wCRV,
                    &(pCallObject->PeerConnectAddr)) &&
                    ((CallObjectLock(hFoundCallObject, &pFoundCallObject) == CS_OK) &&
                    (pFoundCallObject != NULL)))
            {
                // friendly channel close of the pFoundCallObject.
                Q931SendReleaseCompleteMessage(pFoundCallObject,
                    CC_REJECT_UNDEFINED_REASON, &(pFoundCallObject->ConferenceID), NULL, NULL);

                // unlock the call object before calling shutdown
                CallObjectUnlock(pFoundCallObject);
                
                linkLayerShutdown(pFoundCallObject->dwPhysicalId);

                if((CallObjectLock(hFoundCallObject, &pFoundCallObject) != CS_OK) ||
                  (pFoundCallObject == NULL))
                  return;

                // assign the new dwPhysicalId to found object.
                pFoundCallObject->dwPhysicalId = pCallObject->dwPhysicalId;

                // new object should be destroyed.
                CallObjectDestroy(pCallObject);
                pCallObject = pFoundCallObject;
            }
            else
            {
                // The call is a newly established call, so resolve it now.
                pCallObject->bResolved = TRUE;
            }
        }

        Result = Q931ParseMessage((BYTE *)buf, nbytes, pMessage);

#if (defined(_DEBUG) && defined(PCS_COMPLIANCE))
        InteropOutput(Q931Logger, buf, nbytes, Q931LOG_RECEIVED_PDU);
#endif

	// A message is to be ignored (per 5.8.1 and 5.8.2) if:
	//		- protocol discriminator is not recognized 
	//		- message is too short to contain a complete message
	//		  type information element
        if (   Result != CS_OK
			&& Result != CS_MESSAGE_TOO_SHORT
			&& Result != CS_INVALID_PROTOCOL)
		{
            Result = Q931SendStatusMessage(pCallObject, pMessage,
                CAUSE_VALUE_INVALID_MSG);

            Q931DBG((DBGERROR, "Q931ParseMessage(): failed."));

            if(CallObjectValidate(hQ931Call) != CS_OK) 
            {
			        if (buf)
			        {
			            Free(buf);
			        }
              return;
            }

            dwPhysicalId = pCallObject->dwPhysicalId;
            CallObjectUnlock(pCallObject);
            PostReceiveBuffer(dwPhysicalId, buf);
            return;
        }

        if (pMessage->Shift.Present)
        {
            Q931DBG((DBGERROR, "Shift present in message:  dropped."));
            dwPhysicalId = pCallObject->dwPhysicalId;
            CallObjectUnlock(pCallObject);
            PostReceiveBuffer(dwPhysicalId, buf);
            return;
        }

        // If a hooking procedure has been installed,
        // give it first shot at acting on the received PDU.
        // If it returns TRUE, then processing is finished.
        if (gReceivePDUHookProc)
        {
            BOOL bHookProcessedMessage;
        
            bHookProcessedMessage = gReceivePDUHookProc(pMessage,
                pCallObject->hQ931Call, pCallObject->dwListenToken,
                pCallObject->dwUserToken);
        
            if (bHookProcessedMessage)
            {
                if (CallObjectValidate(hQ931Call) == CS_OK)
                {
                    dwPhysicalId = pCallObject->dwPhysicalId;
                    CallObjectUnlock(pCallObject);
                    PostReceiveBuffer(dwPhysicalId, buf);
                }
                return;
            }
        }

        // Message now contains the values of the Q931 PDU elements...
        switch (pMessage->MessageType)
        {
        case SETUPMESSAGETYPE:
            {
                Q931_SETUP_ASN SetupASN;

                if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0))
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data..."));
                    dwPhysicalId = pCallObject->dwPhysicalId;
                    CallObjectUnlock(pCallObject);
                    PostReceiveBuffer(dwPhysicalId, buf);
                    return;
                }

                Q931DBG((DBGTRACE, "ReceiveCallback(): received Setup message..."));
                Result = Q931SetupParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation,
                    pMessage->UserToUser.UserInformationLength, &SetupASN);
                if (Result == CS_OPTION_NOT_IMPLEMENTED)
                {
                    //... maybe callback callcont in later drop.
  
                    // initiate a disconnect sequence from the caller side.
                    if (Q931SendReleaseCompleteMessage(pCallObject,
                            CC_REJECT_TIMER_EXPIRED, NULL, NULL, NULL) != CS_OK)
                    {
                        // nothing to do if this fails.
                    }

                    dwPhysicalId = pCallObject->dwPhysicalId;
                    CallObjectDestroy(pCallObject);
                    linkLayerShutdown(dwPhysicalId);
                    if (buf)
                    {
                        Free(buf);
                        buf = NULL;
                    }
                    return;
                }
                if (Result != CS_OK)
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data."));
                    break;
                }

                // The "CallerAddr is not passed in the PDU, so the
                // only valuable addr to use is the connection addr
                // passed from the link layer and saved into the call
                // object at connect-time.
                SetupASN.CallerAddrPresent = TRUE;
                SetupASN.CallerAddr = pCallObject->PeerConnectAddr;

                // The "CalleeAddr" which is passed in the PDU is ignored
                // by the ASN parser, and supplied by the link layer
                // instead and saved into the call object at connect-time.
                // here, this address is used as the callee addr.
                SetupASN.CalleeAddrPresent = TRUE;
                SetupASN.CalleeAddr = pCallObject->LocalAddr;

                Result = Q931OnCallSetup(pCallObject, pMessage, &SetupASN);

				_FreeSetupASN(&SetupASN);
	        }
            break;
        case RELEASECOMPLMESSAGETYPE:
            {
                Q931_RELEASE_COMPLETE_ASN ReleaseCompleteASN;

                if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0))
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data..."));
                    dwPhysicalId = pCallObject->dwPhysicalId;
                    CallObjectUnlock(pCallObject);
                    PostReceiveBuffer(dwPhysicalId, buf);
                    return;
                }

                Q931DBG((DBGTRACE, "ReceiveCallback(): received ReleaseComplete message..."));
                Result = Q931ReleaseCompleteParseASN(&pCallObject->World,
										pMessage->UserToUser.UserInformation,
                    pMessage->UserToUser.UserInformationLength, &ReleaseCompleteASN);
                if (Result != CS_OK)
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data."));
                    break;
                }
                Result = Q931OnCallReleaseComplete(pCallObject, pMessage, &ReleaseCompleteASN);
                if (CallObjectValidate(hQ931Call) == CS_OK)
                {
                     dwPhysicalId = pCallObject->dwPhysicalId;
                     CallObjectDestroy(pCallObject);
                     linkLayerShutdown(dwPhysicalId);
                }
                Free(buf);
				_FreeReleaseCompleteASN(&ReleaseCompleteASN);
                return;
            }
            break;
        case FACILITYMESSAGETYPE:
            {
                Q931_FACILITY_ASN FacilityASN;

                if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0))
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data..."));
                    dwPhysicalId = pCallObject->dwPhysicalId;
                    CallObjectUnlock(pCallObject);
                    PostReceiveBuffer(dwPhysicalId, buf);
                    return;
                }

                Q931DBG((DBGTRACE, "ReceiveCallback(): received Facility message..."));
                Result = Q931FacilityParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation,
                    pMessage->UserToUser.UserInformationLength, &FacilityASN);
                if (Result != CS_OK)
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data."));
                    break;
                }

                // initiate a disconnect sequence from the caller side.
                Q931SendReleaseCompleteMessage(pCallObject,
                        CC_REJECT_CALL_DEFLECTION, NULL, NULL, NULL);
               
                Result = Q931OnCallFacility(pCallObject, pMessage, &FacilityASN);
               	_FreeFacilityASN(&FacilityASN);
                dwPhysicalId = pCallObject->dwPhysicalId;
                CallObjectDestroy(pCallObject);
                linkLayerShutdown(dwPhysicalId);
                Free(buf);
                return;
            }
            break;
        case CONNECTMESSAGETYPE:
            {
                Q931_CONNECT_ASN ConnectASN;

                if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0))
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data..."));
                    dwPhysicalId = pCallObject->dwPhysicalId;
                    CallObjectUnlock(pCallObject);
                    PostReceiveBuffer(dwPhysicalId, buf);
                    return;
                }

                Q931DBG((DBGTRACE, "ReceiveCallback(): received Connect message..."));
                Result = Q931ConnectParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation,
                    pMessage->UserToUser.UserInformationLength, &ConnectASN);
                if (Result != CS_OK)
                {
                    Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data."));
                    break;
                }
                Result = Q931OnCallConnect(pCallObject, pMessage, &ConnectASN);
				_FreeConnectASN(&ConnectASN);
            }
            break;
        case PROCEEDINGMESSAGETYPE:
            {
                Q931_CALL_PROCEEDING_ASN ProceedingASN;

                Q931DBG((DBGTRACE, "ReceiveCallback(): received Proceeding message..."));
                if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0))
                {
                    Result = Q931OnCallProceeding(pCallObject, pMessage, NULL);
                }
                else
                {
                    Result = Q931ProceedingParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation,
                        pMessage->UserToUser.UserInformationLength, &ProceedingASN);
                    if (Result != CS_OK)
                    {
                        Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data."));
                        break;
                    }
                    Result = Q931OnCallProceeding(pCallObject, pMessage, &ProceedingASN);
					_FreeProceedingASN(&ProceedingASN);
                }
            }
            break;
        case ALERTINGMESSAGETYPE:
            {
                Q931_ALERTING_ASN AlertingASN;

                Q931DBG((DBGTRACE, "ReceiveCallback(): received Alerting message..."));
                if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0))
                {
                    Result = Q931OnCallAlerting(pCallObject, pMessage, NULL);
                }
                else
                {
                    Result = Q931AlertingParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation,
                        pMessage->UserToUser.UserInformationLength, &AlertingASN);
                    if (Result != CS_OK)
                    {
                        Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data."));
                        break;
                    }
                    Result = Q931OnCallAlerting(pCallObject, pMessage, &AlertingASN);
					_FreeAlertingASN(&AlertingASN);
                }
            }
            break;
        case RELEASEMESSAGETYPE:
        case STATUSMESSAGETYPE:
            Q931DBG((DBGWARNING, "ReceiveCallback(): message not yet supported."));
            break;
        case STATUSENQUIRYMESSAGETYPE:
            Q931DBG((DBGWARNING, "ReceiveCallback(): message not yet supported."));
            Result = Q931OnCallStatusEnquiry(pCallObject, pMessage);
            break;
        default:
            Q931DBG((DBGERROR, "ReceiveCallback(): unknown message received."));
            break;
        }

        // re-validate the call object:
        if (CallObjectValidate(hQ931Call) == CS_OK)
        {
            dwPhysicalId = pCallObject->dwPhysicalId;
            CallObjectUnlock(pCallObject);
            PostReceiveBuffer(dwPhysicalId, buf);
            if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
              return;
        }
        else
        {
            if (buf)
            {
                Free(buf);
            }
            return;
        }

        if (   Result == CS_INCOMPATIBLE_VERSION
			|| Result == CS_NO_MEMORY
			|| Result == CS_SUBSYSTEM_FAILURE)
        {
            // initiate a disconnect sequence from the caller side.
            Q931SendReleaseCompleteMessage(pCallObject,
                    CC_REJECT_INVALID_REVISION, NULL, NULL, NULL);
            
            dwPhysicalId = pCallObject->dwPhysicalId;
            CallObjectDestroy(pCallObject);
            linkLayerShutdown(dwPhysicalId);
            return;
        }

        if (Result == CS_MANDATORY_IE_MISSING)
        {
            Q931SendStatusMessage(pCallObject, pMessage,
                CAUSE_VALUE_IE_MISSING);
        }
        else if (   Result == CS_BAD_IE_CONTENT
				 || Result == CS_BAD_PARAM
				 || Result == CS_NO_FIELD_DATA)
        {
            Q931SendStatusMessage(pCallObject, pMessage,
                CAUSE_VALUE_IE_CONTENTS);
        }

    }
    else if (message == LINK_RECV_CLOSED)
    {
        // Socket closed
        if (buf)
        {
            Free(buf);
        }
        pCallObject->Callback(Q931_CALL_CONNECTION_CLOSED, pCallObject->hQ931Call,
            pCallObject->dwListenToken,
            pCallObject->dwUserToken, NULL);

        if (CallObjectValidate(hQ931Call) == CS_OK)
        {
             dwPhysicalId = pCallObject->dwPhysicalId;
             pCallObject->bConnected = FALSE;
						 CallObjectUnlock(pCallObject);
             linkLayerShutdown(dwPhysicalId);
        }
        return;
    }
    else if (buf)
    {
        // unknown condition?
        Free(buf);
    }

    if (CallObjectValidate(hQ931Call) == CS_OK)
    {
        CallObjectUnlock(pCallObject);
    }

    return;
}

//====================================================================================
//====================================================================================
void
Q931ReceiveCallback(DWORD instance, HRESULT message, BYTE *buf, DWORD nbytes)
{
    Q931MESSAGE *pMessage = NULL;
    if (message == LINK_RECV_DATA)
    {
        pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE));
        if (pMessage == NULL)
        {
            Q931DBG((DBGERROR, "Not enough memory to process Q931 message."));
            // something more should be done here to indicate SERIOUS error...
            return;
        }
    }
    OnReceiveCallback(instance, message, pMessage, buf, nbytes);
    if (pMessage)
    {
        Free(pMessage);
    }
    return;
}

//====================================================================================
//====================================================================================
void
Q931ConnectCallback(DWORD dwInstance, HRESULT dwMessage,
        CC_ADDR *pLocalAddr, CC_ADDR *pPeerAddr)
{
    HQ931CALL hQ931Call = (HQ931CALL)dwInstance;
    P_CALL_OBJECT pCallObject = NULL;
    HRESULT TempResult;
    DWORD dwPhysicalId;

    Q931DBG((DBGTRACE, "Entering Q931ConnectCallback()..."));

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error"));
        return;
    }

    pCallObject->bConnected = TRUE;

    if (FAILED(dwMessage))
    {
        // shut down link layer; report failure to client
        CSS_CALL_FAILED EventData;

        Q931DBG((DBGERROR, "error in connect"));

        EventData.error = dwMessage;
        pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call,
            pCallObject->dwListenToken,
            pCallObject->dwUserToken, &EventData);

        if (CallObjectValidate(hQ931Call) == CS_OK)
        {
             DWORD dwId = pCallObject->dwPhysicalId;
             CallObjectDestroy(pCallObject);
             linkLayerShutdown(dwId);
        }
        return;
    }

    if (dwMessage != LINK_CONNECT_COMPLETE)
    {
        Q931DBG((DBGERROR, "unexpected connect callback"));
        CallObjectUnlock(pCallObject);
        return;
    }

    if (pCallObject->bCallState == CALLSTATE_NULL)
    {
        pCallObject->bCallState = CALLSTATE_INITIATED;
    }
    pCallObject->LocalAddr = *pLocalAddr;
    pCallObject->PeerConnectAddr = *pPeerAddr;

    // if the user specified a binary source address with address = 0,
    // fill in the address with the local address and send.
    if ((pCallObject->SourceAddrPresent) &&
            (pCallObject->SourceAddr.nAddrType == CC_IP_BINARY) &&
            (!pCallObject->SourceAddr.Addr.IP_Binary.dwAddr))
    {
        pCallObject->SourceAddr = *pLocalAddr;
    }

    if ((pCallObject->fIsCaller) &&
            (pCallObject->bCallState == CALLSTATE_INITIATED))
    {
        // send the SETUP message to the peer.
        DWORD CodedLengthASN;
        BYTE *CodedPtrASN;
        HRESULT ResultASN = CS_OK;

        DWORD CodedLengthPDU;
        BYTE *CodedPtrPDU;
        HRESULT ResultPDU = CS_OK;

        int nError = 0;
        BOOL ResultSend = FALSE;
        BINARY_STRING UserUserData;
        PCC_VENDORINFO pVendorInfo = NULL;
        CC_NONSTANDARDDATA *pNonStandardData = NULL;
		DWORD dwId;

        if (pCallObject->VendorInfoPresent)
        {
            pVendorInfo = &(pCallObject->VendorInfo);
        }

        if (pCallObject->NonStandardDataPresent)
        {
            pNonStandardData = &(pCallObject->NonStandardData);
        }

        // if there is a special callee alias list, load the calledparty#.
        if (pCallObject->szCalledPartyNumber[0] == 0 &&
            pCallObject->pCalleeAliasList != NULL &&
            pCallObject->pCalleeAliasList->wCount == 1 &&
            pCallObject->pCalleeAliasList->pItems[0].wType == CC_ALIAS_H323_PHONE &&
            pCallObject->pCalleeAliasList->pItems[0].wDataLength > 0 &&
            pCallObject->pCalleeAliasList->pItems[0].pData != NULL)
        {
            PCC_ALIASITEM pItem = &pCallObject->pCalleeAliasList->pItems[0];
            WCHAR szWidePartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1];

            memset(szWidePartyNumber, 0 , CC_MAX_PARTY_NUMBER_LEN + 1);

            if (pItem->wPrefixLength > 0 && pItem->pPrefix != NULL)
            {
                ASSERT((pItem->wPrefixLength + pItem->wDataLength +1) <= (sizeof(szWidePartyNumber)/sizeof(szWidePartyNumber[0])));
                memcpy(&szWidePartyNumber[0],
                       pItem->pPrefix,
                       (pItem->wPrefixLength) * sizeof(WCHAR));
                memcpy(&szWidePartyNumber[pItem->wPrefixLength],
                       pItem->pData,
                       pItem->wDataLength * sizeof(WCHAR));
            }
            else
            {
                ASSERT((pItem->wDataLength +1) <= (sizeof(szWidePartyNumber)/sizeof(szWidePartyNumber[0])));
                memcpy(szWidePartyNumber,
                       pCallObject->pCalleeAliasList->pItems[0].pData,
                       pItem->wDataLength * sizeof(WCHAR));
            }
            WideCharToMultiByte(CP_ACP, 0, szWidePartyNumber,
                pItem->wPrefixLength + pItem->wDataLength * sizeof(WCHAR), 
                pCallObject->szCalledPartyNumber,
                sizeof(pCallObject->szCalledPartyNumber), NULL, NULL);
        }

        // may wish to pass alias parms later instead of NULL, NULL.
        ResultASN = Q931SetupEncodeASN(pNonStandardData,
            pCallObject->SourceAddrPresent ? &(pCallObject->SourceAddr) : NULL,
            pCallObject->PeerCallAddrPresent ? &(pCallObject->PeerCallAddr) : NULL,  // callee
            pCallObject->wGoal,
            pCallObject->wCallType,
            pCallObject->bCallerIsMC,
            &(pCallObject->ConferenceID),
            pCallObject->pCallerAliasList,
            pCallObject->pCalleeAliasList,
            pCallObject->pExtraAliasList,
            pCallObject->pExtensionAliasItem,
            pVendorInfo,
            pCallObject->bIsTerminal,
            pCallObject->bIsGateway,
                                                &pCallObject->World,
            &CodedPtrASN,
            &CodedLengthASN);

        if ((ResultASN != CS_OK) || (CodedLengthASN == 0) ||
                (CodedPtrASN == NULL))
        {
            CSS_CALL_FAILED EventData;
            Q931DBG((DBGERROR, "Q931SetupEncodeASN() failed, nothing to send."));
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            EventData.error = CS_INTERNAL_ERROR;
            dwId = pCallObject->dwPhysicalId;
            pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call,
                pCallObject->dwListenToken,
                pCallObject->dwUserToken, &EventData);
            linkLayerShutdown(dwId);
			if (CallObjectValidate(hQ931Call) == CS_OK)
                 CallObjectDestroy(pCallObject);
            return;
        }

        UserUserData.length = (WORD)CodedLengthASN;
        UserUserData.ptr = CodedPtrASN;

        ResultPDU = Q931SetupEncodePDU(pCallObject->wCRV,
            pCallObject->szDisplay, pCallObject->szCalledPartyNumber,
			pCallObject->dwBandwidth,
            &UserUserData, &CodedPtrPDU, &CodedLengthPDU);

        if (CodedPtrASN != NULL)
        {
            Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
        }

        if ((ResultPDU != CS_OK) || (CodedLengthPDU == 0) ||
                (CodedPtrPDU == NULL))
        {
            CSS_CALL_FAILED EventData;
            Q931DBG((DBGERROR, "Q931SetupEncodePDU() failed, nothing to send."));
            if (CodedPtrPDU != NULL)
            {
                Free(CodedPtrPDU);
            }
            EventData.error = CS_INTERNAL_ERROR;
            dwId = pCallObject->dwPhysicalId;
            pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call,
                pCallObject->dwListenToken,
                pCallObject->dwUserToken, &EventData);
            linkLayerShutdown(dwId);
			if (CallObjectValidate(hQ931Call) == CS_OK)
                 CallObjectDestroy(pCallObject);
            return;
        }

        if (pCallObject->NonStandardDataPresent)
        {
            if (pCallObject->NonStandardData.sData.pOctetString != NULL)
            {
                Free(pCallObject->NonStandardData.sData.pOctetString);
                pCallObject->NonStandardData.sData.pOctetString = NULL;
            }
            pCallObject->NonStandardDataPresent = FALSE;
        }
        Q931FreeAliasNames(pCallObject->pCallerAliasList);
        pCallObject->pCallerAliasList = NULL;
        Q931FreeAliasNames(pCallObject->pCalleeAliasList);
        pCallObject->pCalleeAliasList = NULL;
        Q931FreeAliasNames(pCallObject->pExtraAliasList);
        pCallObject->pExtraAliasList = NULL;
        Q931FreeAliasItem(pCallObject->pExtensionAliasItem);
        pCallObject->pExtensionAliasItem = NULL;

        TempResult=Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE);
        if (CallObjectValidate(hQ931Call) != CS_OK)
          return;

        if(FAILED(TempResult))
        {
            CSS_CALL_FAILED EventData;

            EventData.error = TempResult;
			dwId = pCallObject->dwPhysicalId;
            pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call,
                pCallObject->dwListenToken,
                pCallObject->dwUserToken, &EventData);
            linkLayerShutdown(dwId);
			if (CallObjectValidate(hQ931Call) == CS_OK)
                 CallObjectDestroy(pCallObject);
            return;
        }

        Q931StartTimer(pCallObject, Q931_TIMER_303);
    }
    dwPhysicalId = pCallObject->dwPhysicalId;
    CallObjectUnlock(pCallObject);
    PostReceiveBuffer(dwPhysicalId, NULL);
}

//====================================================================================
//====================================================================================
void
Q931ListenCallback(DWORD dwInstance, HRESULT dwMessage,
        CC_ADDR *LocalAddr, CC_ADDR *PeerAddr)
{
    HQ931LISTEN hListenObject = (HQ931LISTEN)dwInstance;
    P_LISTEN_OBJECT pListenObject = NULL;
    CS_STATUS CreateObjectResult;
    HQ931CALL hQ931Call;
    P_CALL_OBJECT pCallObject = NULL;
    HRESULT TempResult;
	DWORD dwPhysicalId;

    Q931DBG((DBGTRACE, "Q931ListenCallback."));

    if (dwMessage != LINK_CONNECT_REQUEST)
    {
        Q931DBG((DBGERROR, "unexpected callback received on listen socket"));
        return;
    }

    if (FAILED(dwMessage))
    {
        Q931DBG((DBGERROR, "error on listen socket"));
        return;
    }

    if ((ListenObjectLock(hListenObject, &pListenObject) != CS_OK) || (pListenObject == NULL))
    {
        Q931DBG((DBGERROR, "ListenObjectLock() returned error"));
        return;
    }

    // create call object with all known attributes of this call.
    // a handle of the call object is returned in phQ931Call.
    CreateObjectResult = CallObjectCreate(&hQ931Call,
        pListenObject->dwUserToken,
        CC_INVALID_HANDLE,
        pListenObject->ListenCallback,
        FALSE,                  // I am NOT the caller.
        LocalAddr,              // Local address on which channel is connected
        PeerAddr,               // Address to which channel is connected
        NULL,                   // Address of opposite call end-point.
        NULL,                   // no source addr
        NULL,                   // no conference id yet.
        CSG_NONE,               // no goal yet.
        CC_CALLTYPE_UNKNOWN,    // no call type yet.
        FALSE,                  // caller is assumed to not be the MC.
        NULL,                   // no display yet.
        NULL,                   // no called party number yet.
        NULL,                   // no caller aliases yet.
        NULL,                   // no callee aliases yet.
        NULL,                   // no extra aliases yet.
        NULL,                   // no extension aliases.
        NULL,                   // no EndpointType info yet.
        NULL,
		0,						// no bandwidth yet
		0);						// no CRV yet.
    if (CreateObjectResult != CS_OK)
    {
        Q931DBG((DBGERROR, "CallObjectCreate() failed."));
        ListenObjectUnlock(pListenObject);
        return;
    }

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error"));
        ListenObjectUnlock(pListenObject);
        return;
    }

    TempResult = linkLayerInit(&pCallObject->dwPhysicalId, hQ931Call,
        Q931ReceiveCallback, Q931SendComplete);
    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "linkLayerInit() failed"));
        CallObjectDestroy(pCallObject);
        ListenObjectUnlock(pListenObject);
        return;
    }

//    pCallObject->bCallState = CALLSTATE_NULL;

    // unlock CallObject before calling down into h245ws in order to prevent deadlock - which
    // is probably unlikely with linkLayerAccept(), but just to be safe and consistent...
    // not sure if we need to worry about unlocking the listen object???

    dwPhysicalId = pCallObject->dwPhysicalId;
    CallObjectUnlock(pCallObject);

    TempResult = linkLayerAccept(pListenObject->dwPhysicalId,
        dwPhysicalId, Q931ConnectCallback);

    if (FAILED(TempResult))
    {
	      if((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
   	    {
	           ListenObjectUnlock(pListenObject);
	           return;
  	    }
        Q931DBG((DBGERROR, "linkLayerAccept() failed"));
        {
             DWORD dwId = pCallObject->dwPhysicalId;
             CallObjectDestroy(pCallObject);
             linkLayerShutdown(dwId);
        }
        ListenObjectUnlock(pListenObject);
        return;
    }

    ListenObjectUnlock(pListenObject);
}

//====================================================================================
//
// PUBLIC FUNCTIONS
//
//====================================================================================

//====================================================================================
//====================================================================================
CS_STATUS
Q931Init()
{
    CS_STATUS result;

    if (bQ931Initialized == TRUE)
    {
    	ASSERT(FALSE);
        return CS_DUPLICATE_INITIALIZE;
    }

    bQ931Initialized = TRUE;

    // Register Call Setup for debug output
    ISRREGISTERMODULE(&ghISRInst, "Q931", "Q931 Call Setup");

    // Initialize the current conference ID to 0's, which is intentionally
    // assigned to an invalid conference ID.  Must create one for it
    // to be valid.
    memset(&(ConferenceIDSource), 0, sizeof(ConferenceIDSource));

    __try {

        InitializeCriticalSectionAndSpinCount(&(ConferenceIDSource.Lock),H323_SPIN_COUNT);

    } __except ((GetExceptionCode() == STATUS_NO_MEMORY)
                ? EXCEPTION_EXECUTE_HANDLER
                : EXCEPTION_CONTINUE_SEARCH
                ) {

        // failure
        return CS_NO_MEMORY;
    }

    if ((result = ListenListCreate()) != CS_OK)
    {
        return result;
    }
    if ((result = CallListCreate()) != CS_OK)
    {
        ListenListDestroy();
        return result;
    }

#if (defined(_DEBUG) && defined(PCS_COMPLIANCE))
    Q931Logger = InteropLoad(Q931LOG_PROTOCOL);
#endif

    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931DeInit()
{
    CS_STATUS result1;
    CS_STATUS result2;

    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

#if (defined(_DEBUG) && defined(PCS_COMPLIANCE))
// This causes a protection exception, so don't do it for now.  DAC 12/9/96
//    InteropUnload(Q931Logger);
#endif

    result1 = ListenListDestroy();

    result2 = CallListDestroy();

    DeleteCriticalSection(&(ConferenceIDSource.Lock));

    bQ931Initialized = FALSE;

    if (result1 != CS_OK)
    {
        return result1;
    }
    return result2;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931Listen(
    PHQ931LISTEN        phQ931Listen,
    PCC_ADDR               pListenAddr,
    DWORD               dwListenToken,
    Q931_CALLBACK       ListenCallback)
{
    CS_STATUS CreateObjectResult;
    P_LISTEN_OBJECT pListenObject = NULL;
    HRESULT TempResult;

    // make sure q931 is initialized with an initialize flag.
    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    // make sure parms are validated.
    if ((phQ931Listen == NULL) || (ListenCallback == NULL) || (pListenAddr == NULL))
    {
        ASSERT(FALSE);
        return CS_BAD_PARAM;
    }

    SetDefaultPort(pListenAddr);

    // create listen object with all known attributes of this listen session.
    // a handle of the listen object is returned in phQ931Listen.
    
    CreateObjectResult = ListenObjectCreate(phQ931Listen, dwListenToken, ListenCallback);
    if (CreateObjectResult != CS_OK)
    {
        return CS_SUBSYSTEM_FAILURE;
    }

    if (ListenObjectLock(*phQ931Listen, &pListenObject) != CS_OK)
    {
        return CS_BAD_PARAM;
    }

    TempResult = linkLayerListen(&pListenObject->dwPhysicalId, *phQ931Listen,
        pListenAddr, Q931ListenCallback);
    ListenObjectUnlock(pListenObject);
    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "Q931Listen() linkLayerListen failed."));
        return TempResult;
    }

    Q931DBG((DBGTRACE, "Q931Listen() completed successfully."));
    return CS_OK;
}

//====================================================================================
// In the old code, this blocked until thread and socket were finished
// closing...
//====================================================================================
CS_STATUS
Q931CancelListen(
    HQ931LISTEN         hQ931Listen)
{
    P_LISTEN_OBJECT pListenObject = NULL;
    CS_STATUS Status;

    // make sure q931 is initialized with an initialize flag.
    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Q931CancelListen() finding listen object..."));

    // lock the listen object, get the event to wait for, and unlock the listen object.
    if (ListenObjectLock(hQ931Listen, &pListenObject) != CS_OK)
    {
        return CS_BAD_PARAM;
    }

    {
        DWORD dwId = pListenObject->dwPhysicalId;
        linkLayerShutdown(dwId);
        // destroy the object.  dont need to unlock it since entire object will be destroyed.
        Q931DBG((DBGTRACE, "Q931CancelListen(): destroying listen object..."));
        Status = ListenObjectDestroy(pListenObject);
    }

    return Status;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931PlaceCall(
    PHQ931CALL phQ931Call,
    LPWSTR pszDisplay,
    PCC_ALIASNAMES pCallerAliasList,
    PCC_ALIASNAMES pCalleeAliasList,
    PCC_ALIASNAMES pExtraAliasList,
    PCC_ALIASITEM pExtensionAliasItem,
    PCC_NONSTANDARDDATA pNonStandardData,
    PCC_ENDPOINTTYPE pSourceEndpointType,
    LPWSTR pszCalledPartyNumber,
    PCC_ADDR pControlAddr,
    PCC_ADDR pDestinationAddr,
    PCC_ADDR pSourceAddr,
    BOOL bCallerIsMC,
    CC_CONFERENCEID *pConferenceID,
    WORD wGoal,
    WORD wCallType,
    DWORD dwUserToken,
    Q931_CALLBACK ConnectCallback,
	DWORD dwBandwidth,
    WORD wCRV)
{
    CS_STATUS CreateObjectResult;
    P_CALL_OBJECT pCallObject = NULL;
    CC_ADDR PeerCallAddr;
    CC_ADDR PeerConnectAddr;
    CC_ADDR SourceAddr;
    HRESULT TempResult;
    char szAsciiDisplay[CC_MAX_DISPLAY_LENGTH + 1];
    char szAsciiPartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1];
    DWORD dwPhysicalId;

    // make sure q931 is initialized with an initialize flag.
    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    // make sure parms are validated.
    if ((phQ931Call == NULL) || (ConnectCallback == NULL) ||
            ((pControlAddr == NULL) && (pDestinationAddr == NULL)) ||
            (pSourceEndpointType == NULL))
    {
        return CS_BAD_PARAM;
    }

    {
        CS_STATUS TempStatus;

        TempStatus = Q931ValidateAddr(pControlAddr);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateAddr(pDestinationAddr);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateAddr(pSourceAddr);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }

        TempStatus = Q931ValidateVendorInfo(pSourceEndpointType->pVendorInfo);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateDisplay(pszDisplay);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidatePartyNumber(pszCalledPartyNumber);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }

        szAsciiDisplay[0] = '\0';
        if (pszDisplay && WideCharToMultiByte(CP_ACP, 0, pszDisplay, -1, szAsciiDisplay,
                sizeof(szAsciiDisplay), NULL, NULL) == 0)
        {
            return CS_BAD_PARAM;
        }
        szAsciiPartyNumber[0] = '\0';
        if (pszCalledPartyNumber && WideCharToMultiByte(CP_ACP, 0, pszCalledPartyNumber, -1, szAsciiPartyNumber,
                sizeof(szAsciiPartyNumber), NULL, NULL) == 0)
        {
            return CS_BAD_PARAM;
        }
        TempStatus = Q931ValidateNonStandardData(pNonStandardData);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateAliasNames(pCallerAliasList);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateAliasNames(pCalleeAliasList);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateAliasNames(pExtraAliasList);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateAliasItem(pExtensionAliasItem);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
    }

    // get the correct callee and control address to use for the call.
    if (pDestinationAddr)
    {
        if (!MakeBinaryADDR(pDestinationAddr, &PeerCallAddr))
        {
            return CS_BAD_PARAM;
        }
        SetDefaultPort(&PeerCallAddr);
    }

    if (pControlAddr)
    {
        if (!MakeBinaryADDR(pControlAddr, &PeerConnectAddr))
        {
            return CS_BAD_PARAM;
        }
        SetDefaultPort(&PeerConnectAddr);
    }
    else
    {
        PeerConnectAddr = PeerCallAddr;
    }

    // get the correct callee and control address to use for the call.
    if (pSourceAddr)
    {
        if (!MakeBinaryADDR(pSourceAddr, &SourceAddr))
        {
            return CS_BAD_PARAM;
        }
        SetDefaultPort(&SourceAddr);
    }

	if (wGoal == CSG_CREATE)
        {
            // caller is asking to start a new conference.
            if (((DWORD *)pConferenceID->buffer)[0] == 0 && 
                ((DWORD *)pConferenceID->buffer)[1] == 0 &&
                ((DWORD *)pConferenceID->buffer)[2] == 0 &&
                ((DWORD *)pConferenceID->buffer)[3] == 0)
            {
                _ConferenceIDNew(pConferenceID);
            }
        }

    // create call object with all known attributes of this call.
    // a handle of the call object is returned in phQ931Call.
    CreateObjectResult = CallObjectCreate(phQ931Call,
        CC_INVALID_HANDLE,
        dwUserToken,
        ConnectCallback,
        TRUE,                  // I am the caller.
        NULL,                  // no local address yet.
        &PeerConnectAddr,
        pDestinationAddr ? &PeerCallAddr : NULL,
        pSourceAddr ? &SourceAddr : NULL,
        pConferenceID,
        wGoal,
        wCallType,
        bCallerIsMC,
        pszDisplay ? szAsciiDisplay : NULL,
        pszCalledPartyNumber ? szAsciiPartyNumber : NULL,
        pCallerAliasList,
        pCalleeAliasList,
        pExtraAliasList,
        pExtensionAliasItem,
        pSourceEndpointType,
        pNonStandardData,
		dwBandwidth,
        wCRV);

    if (CreateObjectResult != CS_OK)
    {
        return CS_SUBSYSTEM_FAILURE;
    }

    if ((CallObjectLock(*phQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error"));
        return CS_SUBSYSTEM_FAILURE;
    }

    TempResult = linkLayerInit(&pCallObject->dwPhysicalId, *phQ931Call,
        Q931ReceiveCallback, Q931SendComplete);
    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "linkLayerInit() failed"));
        CallObjectDestroy(pCallObject);
        *phQ931Call = 0;
        return TempResult;
    }

    // unlock CallObject before calling down into h245ws in order to prevent deadlock - which
    // is probably unlikely with linkLayerConnect(), but just to be safe and consistent...
    dwPhysicalId = pCallObject->dwPhysicalId;
    CallObjectUnlock(pCallObject);
    TempResult = linkLayerConnect(dwPhysicalId, &PeerConnectAddr,
            Q931ConnectCallback);
    if((CallObjectLock(*phQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        *phQ931Call = 0;
        return(CS_INTERNAL_ERROR);
    }

    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "linkLayerConnect() failed"));
        {
             DWORD dwId = pCallObject->dwPhysicalId;
             CallObjectDestroy(pCallObject);
             linkLayerShutdown(dwId);
        }
        *phQ931Call = 0;
        return TempResult;
    }

//    pCallObject->bCallState = CALLSTATE_NULL;

    CallObjectUnlock(pCallObject);

    Q931DBG((DBGTRACE, "Q931PlaceCall() completed successfully."));
    return CS_OK;
}

//====================================================================================
// In the old code, this blocked until thread and socket were finished
// closing...
//====================================================================================
CS_STATUS
Q931Hangup(
    HQ931CALL hQ931Call,
    BYTE bReason)
{
    P_CALL_OBJECT pCallObject = NULL;
    CS_STATUS Status;

    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Entering Q931Hangup()..."));

    // need parameter checking...
    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGTRACE, "Call Object no longer available: 0x%08lx", hQ931Call));
        return CS_BAD_PARAM;
    }

    {
    
        CS_STATUS SendStatus = CS_OK;
        if (pCallObject->bCallState != CALLSTATE_NULL)
        {
            // send the RELEASE COMPLETE message to the peer to hang-up.
            SendStatus = Q931SendReleaseCompleteMessage(pCallObject,
                bReason, &(pCallObject->ConferenceID), NULL, NULL);
        }

        {
             DWORD dwId = pCallObject->dwPhysicalId;
             Status = CallObjectDestroy(pCallObject);
             linkLayerShutdown(dwId);
        }

        if (FAILED(SendStatus))
        {
            return SendStatus;
        }
    }

    return Status;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931AcceptCall(
    HQ931CALL           hQ931Call,
    LPWSTR              pszDisplay,
    PCC_NONSTANDARDDATA pNonStandardData,
    PCC_ENDPOINTTYPE    pDestinationEndpointType,
    PCC_ADDR            pH245Addr,
	DWORD				dwBandwidth,
    DWORD               dwUserToken)
{
    P_CALL_OBJECT pCallObject = NULL;
    CS_STATUS result = CS_OK;
    char szAsciiDisplay[CC_MAX_DISPLAY_LENGTH + 1];

    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Entering Q931AcceptCall()..."));

    if ((pDestinationEndpointType == NULL) ||
            (pDestinationEndpointType->pVendorInfo == NULL))
    {
        return CS_BAD_PARAM;
    }

    {
        CS_STATUS TempStatus;

        TempStatus = Q931ValidateVendorInfo(pDestinationEndpointType->pVendorInfo);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        TempStatus = Q931ValidateDisplay(pszDisplay);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
        szAsciiDisplay[0] = '\0';
        if (pszDisplay && WideCharToMultiByte(CP_ACP, 0, pszDisplay, -1, szAsciiDisplay,
                sizeof(szAsciiDisplay), NULL, NULL) == 0)
        {
            return CS_BAD_PARAM;
        }
        TempStatus = Q931ValidateNonStandardData(pNonStandardData);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
    }

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error (Socket not found)."));
        return CS_INTERNAL_ERROR;
    }

    if (pCallObject->fIsCaller)
    {
        Q931DBG((DBGERROR, "Caller attempted to accept call."));

        CallObjectUnlock(pCallObject);
        return CS_OUT_OF_SEQUENCE;
    }

    // label with the user supplied UserToken for this call object.
    pCallObject->dwUserToken = dwUserToken;
	pCallObject->dwBandwidth = dwBandwidth;

    // send the CONNECT message to peer to accept call.
    {
        DWORD CodedLengthASN;
        BYTE *CodedPtrASN;
        HRESULT ResultASN = CS_OK;
        CC_ADDR h245Addr;

        if (pH245Addr != NULL)
        {
            MakeBinaryADDR(pH245Addr, &h245Addr);
        }

        ResultASN = Q931ConnectEncodeASN(pNonStandardData,
            &(pCallObject->ConferenceID),
            (pH245Addr ? &h245Addr : NULL),
            pDestinationEndpointType,
                                                &pCallObject->World,
            &CodedPtrASN,
            &CodedLengthASN);
        if ((ResultASN != CS_OK) || (CodedLengthASN == 0) ||
                (CodedPtrASN == NULL))
        {
            Q931DBG((DBGERROR, "Q931ConnectEncodeASN() failed, nothing to send."));
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            CallObjectUnlock(pCallObject);
            return CS_SUBSYSTEM_FAILURE;
        }
        else
        {
            DWORD CodedLengthPDU;
            BYTE *CodedPtrPDU;
            BINARY_STRING UserUserData;
            HRESULT ResultEncode = CS_OK;
            HRESULT TempResult;
            WORD wCRV = (WORD)(pCallObject->wCRV | 0x8000);

            UserUserData.length = (WORD)CodedLengthASN;
            UserUserData.ptr = CodedPtrASN;

            ResultEncode = Q931ConnectEncodePDU(wCRV,
                szAsciiDisplay, pCallObject->dwBandwidth,
				&UserUserData, &CodedPtrPDU, &CodedLengthPDU);
            if (CodedPtrASN != NULL)
            {
                Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN);
            }
            if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) ||
                    (CodedPtrPDU == NULL))
            {
                Q931DBG((DBGERROR, "Q931ConnectEncodePDU() failed, nothing to send."));
                if (CodedPtrPDU != NULL)
                {
                    Free(CodedPtrPDU);
                }
                CallObjectUnlock(pCallObject);
                return CS_SUBSYSTEM_FAILURE;
            }

            TempResult = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE);
            if (CallObjectValidate(hQ931Call) != CS_OK)
              return CS_INTERNAL_ERROR;


            if (FAILED(TempResult))
            {
                Q931DBG((DBGERROR, "datalinkSendRequest() failed"));

                // when the connect notification fails...what should we do anyway????
                CallObjectUnlock(pCallObject);
                return TempResult;
            }
        }
    }

    pCallObject->bCallState = CALLSTATE_ACTIVE;

    CallObjectUnlock(pCallObject);
    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931RejectCall(
    HQ931CALL hQ931Call,
    BYTE bRejectReason,
    PCC_CONFERENCEID pConferenceID,
    PCC_ADDR pAlternateAddr,
    PCC_NONSTANDARDDATA pNonStandardData)
{
    P_CALL_OBJECT pCallObject = NULL;
    CS_STATUS result = CS_OK;
    CS_STATUS Status = CS_OK;

    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Entering Q931RejectCall()..."));

    {
        CS_STATUS TempStatus;

        TempStatus = Q931ValidateNonStandardData(pNonStandardData);
        if (TempStatus != CS_OK)
        {
            return TempStatus;
        }
    }

    // if reason is alternate addr, but there is no alternate addr -->err
    if (((bRejectReason == CC_REJECT_ROUTE_TO_GATEKEEPER) ||
            (bRejectReason == CC_REJECT_CALL_FORWARDED) ||
            (bRejectReason == CC_REJECT_ROUTE_TO_MC)) &&
            (pAlternateAddr == NULL))
    {
        return CS_BAD_PARAM;
    }

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error (Socket not found)."));
        return CS_INTERNAL_ERROR;
    }

    if (pCallObject->fIsCaller)
    {
        Q931DBG((DBGERROR, "Caller attempted to reject call."));

        CallObjectUnlock(pCallObject);
        return CS_OUT_OF_SEQUENCE;
    }

    result = Q931SendReleaseCompleteMessage(pCallObject,
        bRejectReason, pConferenceID, pAlternateAddr, pNonStandardData);

    {
        DWORD dwId = pCallObject->dwPhysicalId;
        Status = CallObjectDestroy(pCallObject);
        linkLayerShutdown(dwId);
    }

    if (result != CS_OK)
    {
        return result;
    }


    return Status;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931ReOpenConnection(
    HQ931CALL hQ931Call)
{
    P_CALL_OBJECT pCallObject = NULL;
    HRESULT TempResult = CS_OK;
    CC_ADDR PeerConnectAddr;
    DWORD dwPhysicalId;

    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Entering Q931ReOpenConnection()..."));

    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGERROR, "CallObjectLock() returned error."));
        return CS_INTERNAL_ERROR;
    }

    if (pCallObject->bConnected)
    {
        return CS_OUT_OF_SEQUENCE;
    }

    Q931MakePhysicalID(&pCallObject->dwPhysicalId);
    TempResult = linkLayerInit(&pCallObject->dwPhysicalId, hQ931Call,
        Q931ReceiveCallback, Q931SendComplete);
    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "linkLayerInit() failed on re-connect."));
        CallObjectUnlock(pCallObject);
        return TempResult;
    }

    // unlock CallObject before calling down into h245ws in order to prevent deadlock - which
    // is probably unlikely with linkLayerConnect, but just to be safe and consistent...
    
    // copy stuff we need out of call object before we unlock it
    dwPhysicalId = pCallObject->dwPhysicalId;
    PeerConnectAddr = pCallObject->PeerConnectAddr;

    CallObjectUnlock(pCallObject);

    TempResult = linkLayerConnect(dwPhysicalId,
            &PeerConnectAddr, Q931ConnectCallback);

    if((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        return(CS_INTERNAL_ERROR);
    }

    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "linkLayerConnect() failed on re-connect."));
        linkLayerShutdown(pCallObject->dwPhysicalId);
        CallObjectUnlock(pCallObject);
        return TempResult;
    }

    CallObjectUnlock(pCallObject);

    Q931DBG((DBGTRACE, "Q931ReOpenConnection() completed successfully."));
    return CS_OK;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931GetVersion(
    WORD wLength,
    LPWSTR pszVersion)
{
WCHAR	pszQ931Version[255];

    // parameter validation.
    if ((wLength == 0) || (pszVersion == NULL))
    {
        return CS_BAD_PARAM;
    }

    wcscpy(pszQ931Version, L"Call Setup ");
    wcscat(pszQ931Version, Unicode(__DATE__));
    wcscat(pszQ931Version, L" ");
    wcscat(pszQ931Version, Unicode(__TIME__));

    if (wcslen(pszQ931Version) >= wLength)
    {
		memcpy(pszVersion, pszQ931Version, (wLength-1)*sizeof(WCHAR));
        pszQ931Version[wLength-1] = L'\0';
        return CS_BAD_SIZE;
    }

	wcscpy(pszVersion, pszQ931Version);
    return CS_OK;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Timer Routines...
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

//====================================================================================
// Timer 301 has expired for this object...
//====================================================================================
void
CallBackT301(P_CALL_OBJECT pCallObject)
{
    CSS_CALL_FAILED EventData;
    HQ931CALL hQ931Call = pCallObject->hQ931Call;

    EventData.error = CS_RINGING_TIMER_EXPIRED;
    pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call,
        pCallObject->dwListenToken,
        pCallObject->dwUserToken, &EventData);

    if (CallObjectValidate(hQ931Call) == CS_OK)
    {
        if (Q931SendReleaseCompleteMessage(pCallObject,
            CC_REJECT_TIMER_EXPIRED, NULL, NULL, NULL) == CS_OK)
        {
            // nothing to do...
        }
        
        {
             DWORD dwId = pCallObject->dwPhysicalId;
             CallObjectDestroy(pCallObject);
             linkLayerShutdown(dwId);
        }
    }
    return;
}

//====================================================================================
// Timer 303 has expired for this object...
//====================================================================================
void
CallBackT303(P_CALL_OBJECT pCallObject)
{
    CSS_CALL_FAILED EventData;
    HQ931CALL hQ931Call = pCallObject->hQ931Call;

    EventData.error = CS_SETUP_TIMER_EXPIRED;
    pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call,
        pCallObject->dwListenToken,
        pCallObject->dwUserToken, &EventData);

    if (CallObjectValidate(hQ931Call) == CS_OK)
    {
        if (Q931SendReleaseCompleteMessage(pCallObject,
            CC_REJECT_TIMER_EXPIRED, NULL, NULL, NULL) == CS_OK)
        {
            // nothing to do...
        }
        
        {
             DWORD dwId = pCallObject->dwPhysicalId;
             CallObjectDestroy(pCallObject);
             linkLayerShutdown(dwId);
        }
    }
    return;
}

//====================================================================================
//====================================================================================
void
Q931SetReceivePDUHook(Q931_RECEIVE_PDU_CALLBACK Q931ReceivePDUCallback)
{
    gReceivePDUHookProc = Q931ReceivePDUCallback;
    return;
}

//====================================================================================
//====================================================================================
CS_STATUS
Q931FlushSendQueue(
    HQ931CALL hQ931Call)
{
    P_CALL_OBJECT pCallObject = NULL;
    HRESULT TempResult = CS_OK;
    DWORD dwPhysicalId;

    if (bQ931Initialized == FALSE)
    {
        return CS_NOT_INITIALIZED;
    }

    Q931DBG((DBGTRACE, "Entering Q931FlushSendQueue()..."));

    // need parameter checking...
    if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))
    {
        Q931DBG((DBGTRACE, "Call Object no longer available: 0x%08lx", hQ931Call));
        return CS_INTERNAL_ERROR;
    }

    dwPhysicalId = pCallObject->dwPhysicalId;

    CallObjectUnlock(pCallObject);

    TempResult = linkLayerFlushChannel(dwPhysicalId, DATALINK_TRANSMIT);
    if (FAILED(TempResult))
    {
        Q931DBG((DBGERROR, "datalinkSendRequest() failed"));
    }

    return(TempResult);
}

#ifdef __cplusplus
}
#endif