3180 lines
102 KiB
C
3180 lines
102 KiB
C
/****************************************************************************
|
|
*
|
|
* $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
|