You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3835 lines
119 KiB
3835 lines
119 KiB
/*
|
|
* File: ctrlh323.cpp
|
|
*
|
|
* Implementation of IControlChannel using H.323 call control protocol
|
|
* via apis of CALLCONT.DLL
|
|
*
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 09/06/96 mikev created
|
|
*
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include "ctrlh323.h"
|
|
#include "version.h"
|
|
#include "strutil.h"
|
|
|
|
#ifdef DEBUG
|
|
VOID DumpChannelParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2);
|
|
VOID DumpNonstdParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2);
|
|
#else
|
|
#define DumpNonstdParameters(a, b)
|
|
#define DumpChannelParameters(a, b)
|
|
#endif
|
|
|
|
extern HRESULT AllocTranslatedAliasList(PCC_ALIASNAMES *ppDest, P_H323ALIASLIST pSource);
|
|
extern VOID FreeTranslatedAliasList(PCC_ALIASNAMES pDoomed);
|
|
|
|
static char DefaultProductID[] = H323_PRODUCTNAME_STR;
|
|
static char DefaultProductVersion[] = H323_PRODUCTRELEASE_STR;
|
|
|
|
HRESULT CCConferenceCallback (BYTE bIndication,
|
|
HRESULT hStatus,
|
|
CC_HCONFERENCE hConference,
|
|
DWORD_PTR dwConferenceToken,
|
|
PCC_CONFERENCE_CALLBACK_PARAMS pConferenceCallbackParams);
|
|
|
|
|
|
VOID CCListenCallback (HRESULT hStatus,PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams);
|
|
|
|
VOID CH323Ctrl::DoAdvise(DWORD dwEvent, LPVOID lpvData)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::DoAdvise");
|
|
|
|
if(IsReleasing()) // don't call out while releasing because it could call
|
|
// back in!
|
|
{
|
|
ERRORMESSAGE(("%s:in releasing state\r\n",_fx_));
|
|
return;
|
|
}
|
|
|
|
AddRef(); // protect ourselves from reentrant calls to Release().
|
|
if(m_pConfAdvise)
|
|
{
|
|
hrLast = m_pConfAdvise->OnControlEvent(dwEvent, lpvData, this);
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s:Invalid m_pConfAdvise\r\n",_fx_));
|
|
}
|
|
|
|
Release();
|
|
}
|
|
|
|
VOID CH323Ctrl::GoNextPhase(CtlChanStateType phase)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::GoNextPhase");
|
|
BOOL fNotifyReady = FALSE;
|
|
#define InvError() ERRORMESSAGE(("%s:Invalid transition from %d to %d\r\n",_fx_,m_Phase,phase))
|
|
switch(phase)
|
|
{
|
|
case CCS_Idle:
|
|
if(m_Phase != CCS_Idle && m_Phase != CCS_Disconnecting && m_Phase != CCS_Listening)
|
|
{
|
|
InvError();
|
|
}
|
|
else
|
|
{
|
|
m_ChanFlags &= ~(CTRLF_OPEN);
|
|
}
|
|
break;
|
|
case CCS_Connecting:
|
|
if((m_Phase != CCS_Idle) && (m_Phase != CCS_Ringing))
|
|
{
|
|
InvError();
|
|
}
|
|
break;
|
|
case CCS_Accepting:
|
|
if(m_Phase != CCS_Listening)
|
|
{
|
|
InvError();
|
|
}
|
|
|
|
break;
|
|
case CCS_Ringing:
|
|
// transition from CCS_Idle state is actually only valid if
|
|
// there is an incoming call
|
|
if(m_Phase != CCS_Connecting && m_Phase != CCS_Filtering && m_Phase != CCS_Listening)
|
|
{
|
|
InvError();
|
|
}
|
|
break;
|
|
case CCS_Opening:
|
|
if(m_Phase != CCS_Connecting && m_Phase != CCS_Accepting
|
|
&& m_Phase != CCS_Ringing)
|
|
{
|
|
InvError();
|
|
}
|
|
break;
|
|
case CCS_Closing:
|
|
if(m_Phase != CCS_Opening && m_Phase != CCS_Ready && m_Phase != CCS_InUse)
|
|
{
|
|
InvError();
|
|
}
|
|
break;
|
|
case CCS_Ready:
|
|
// can be reentered. if notification is already pending, (state is
|
|
// already CCS_InUse) stay there, else do the transition
|
|
if(m_Phase != CCS_InUse)
|
|
{
|
|
if(m_Phase != CCS_Opening)
|
|
{
|
|
InvError();
|
|
}
|
|
else
|
|
{
|
|
//signal "all channels ready" to IConfAdvise
|
|
fNotifyReady = TRUE;
|
|
}
|
|
}
|
|
phase = CCS_InUse;
|
|
break;
|
|
case CCS_InUse:
|
|
// previous state must be CCS_InUse or CCS_Ready
|
|
if(m_Phase != CCS_InUse && m_Phase != CCS_Ready)
|
|
{
|
|
InvError();
|
|
}
|
|
|
|
break;
|
|
case CCS_Listening:
|
|
if(m_Phase != CCS_Idle)
|
|
{
|
|
InvError();
|
|
}
|
|
break;
|
|
case CCS_Disconnecting:
|
|
//if(m_Phase != CCS_Closing)
|
|
//{
|
|
// InvError();
|
|
//}
|
|
break;
|
|
|
|
}
|
|
|
|
m_Phase = phase;
|
|
|
|
if (fNotifyReady)
|
|
{
|
|
DoAdvise(CCEV_ALL_CHANNELS_READY, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CCConferenceCallback (BYTE bIndication,
|
|
HRESULT hConfStatus, CC_HCONFERENCE hConference, DWORD_PTR dwConferenceToken,
|
|
PCC_CONFERENCE_CALLBACK_PARAMS pConferenceCallbackParams)
|
|
{
|
|
HRESULT hr = CC_NOT_IMPLEMENTED;
|
|
FX_ENTRY ("CCConferenceCallback ");
|
|
CH323Ctrl *pConnection = (CH323Ctrl *)dwConferenceToken;
|
|
|
|
if(IsBadWritePtr(pConnection, sizeof(CH323Ctrl)))
|
|
{
|
|
ERRORMESSAGE(("%s:invalid conf token: 0x%08lx\r\n",_fx_, dwConferenceToken));
|
|
return CC_NOT_IMPLEMENTED; // must be either CC_NOT_IMPLEMENTED or CC_OK.
|
|
}
|
|
|
|
if(pConnection && pConnection->GetConfHandle() == hConference)
|
|
{
|
|
|
|
if(pConnection->IsReleasing())
|
|
{
|
|
// we are in the cleanup path. The object is being deleted without
|
|
// waiting for asynchronous stuff to complete, and we called that one
|
|
// final API (most likely Hangup()) that resulted in a callback. Don't call
|
|
// back into the object.
|
|
DEBUGMSG(ZONE_CONN,("%s:callback while releasing:0x%08lx, hconf:0x%08lx\r\n",_fx_,
|
|
pConnection, hConference));
|
|
return hr;
|
|
}
|
|
pConnection->AddRef(); // protect against Release()ing while not in
|
|
// a quiescent state. We do not want to be
|
|
// released while inside ourself
|
|
hr = pConnection->ConfCallback(bIndication, hConfStatus, pConferenceCallbackParams);
|
|
pConnection->Release();
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
if(pConnection)
|
|
DEBUGMSG(ZONE_CONN,("%s:hConference mismatch, hConference:0x%08lx, object hconf:0x%08lx, pObject:0x%08lx\r\n",_fx_,
|
|
hConference, pConnection->GetConfHandle(), pConnection));
|
|
else
|
|
DEBUGMSG(ZONE_CONN,("%s:null dwConferenceToken\r\n",_fx_));
|
|
}
|
|
#endif //DEBUG
|
|
return hr;
|
|
}
|
|
|
|
VOID CCListenCallback (HRESULT hStatus,PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams)
|
|
{
|
|
FX_ENTRY ("CCListenCallback");
|
|
CH323Ctrl *pConnection;
|
|
if(!pListenCallbackParams)
|
|
{
|
|
return;
|
|
}
|
|
pConnection = (CH323Ctrl *)pListenCallbackParams->dwListenToken;
|
|
|
|
if(IsBadWritePtr(pConnection, sizeof(CH323Ctrl)))
|
|
{
|
|
ERRORMESSAGE(("%s:invalid listen token: 0x%08lx\r\n",_fx_, pListenCallbackParams->dwListenToken));
|
|
return;
|
|
}
|
|
|
|
// BUGBUG there's no hListen passed in - we can't validate it
|
|
// if(pConnection && (pConnection->GetListenHandle() == pListenCallbackParams->h??????))
|
|
|
|
if(pConnection)
|
|
{
|
|
pConnection->AddRef(); // protect against Release()ing while not in
|
|
// a quiescent state. We do not want to be
|
|
// released while inside ourself
|
|
pConnection->ListenCallback(hStatus,pListenCallbackParams);
|
|
pConnection->Release();
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s:null listen token\r\n",_fx_));
|
|
}
|
|
|
|
}
|
|
VOID CH323Ctrl::ListenCallback (HRESULT hStatus,PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::ListenCallback");
|
|
HRESULT hr;
|
|
if(hStatus != CC_OK)
|
|
{
|
|
m_hCallCompleteCode = CCCI_LOCAL_ERROR;
|
|
CH323Ctrl * pAcceptingConnection = NULL;
|
|
BOOL bDisconnect = FALSE;
|
|
|
|
ERRORMESSAGE(("%s:error 0x%08lx\r\n",_fx_,hStatus));
|
|
// aaaaghhh!!! an unsolicited error!!!!!
|
|
// MikeV 10/12/96 - observed behavior is that this will occur if the caller disconnects
|
|
// before the call is accepted (or during acceptance - if a BP is set before the call
|
|
// to AcceptRejectConnection(), the caller times out. But even after that, tracing
|
|
// over AcceptRejectConnection() shown no error is returned. This is bad, because
|
|
// it is hard to tell if this error needs cleaning up after. The error code in
|
|
// that case is 0xa085a001, which is CC_PEER_REJECT
|
|
|
|
// We also don't know if another object has been created to accept the connection
|
|
// or if this is being called in the context of the object that was created and
|
|
// its handle passed to AcceptRejectConnection(). The typical behavior is that it
|
|
// is called in the context of the listening object.
|
|
|
|
// once the accepting object is located, need to check state to see if
|
|
// connection is in the process of being accepted. Find accepting object
|
|
// by matching pListenCallbackParams->ConferenceID;
|
|
|
|
// see if this is the correct context
|
|
if(memcmp(&pListenCallbackParams->ConferenceID, &m_ConferenceID, sizeof(m_ConferenceID))==0)
|
|
{
|
|
// check the current state. If in the process of accepting
|
|
// (either Idle, or filtering), change state to CCS_Closing to make
|
|
// cleanup occur. If already accepted (accepting or ringing), initiate
|
|
// InternalDisconnect(). This should never happen in any other state.
|
|
|
|
// EnterCriticalSection() // LOOKLOOK - NYI
|
|
switch(m_Phase)
|
|
{
|
|
case CCS_Idle:
|
|
case CCS_Filtering:
|
|
break;
|
|
default:
|
|
case CCS_Ringing:
|
|
case CCS_Accepting:
|
|
bDisconnect = TRUE;
|
|
switch(hStatus)
|
|
{
|
|
case CC_PEER_REJECT:
|
|
m_hCallCompleteCode = CCCI_REJECTED;
|
|
ERRORMESSAGE(("%s:Received CC_PEER_REJECT in state %d\r\n",_fx_,m_Phase));
|
|
break;
|
|
|
|
default:
|
|
case CC_INTERNAL_ERROR:
|
|
m_hCallCompleteCode = CCCI_LOCAL_ERROR;
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
// ExitCriticalSection()
|
|
if(bDisconnect)
|
|
InternalDisconnect();
|
|
}
|
|
else
|
|
{
|
|
hr = m_pConfAdvise->FindAcceptingObject((LPIControlChannel *)&pAcceptingConnection,
|
|
&pListenCallbackParams->ConferenceID);
|
|
if(HR_SUCCEEDED(hr) && pAcceptingConnection)
|
|
{
|
|
// call this function in the correct context
|
|
pAcceptingConnection->AddRef();
|
|
pAcceptingConnection->ListenCallback (hStatus, pListenCallbackParams);
|
|
pAcceptingConnection->Release();
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s:conference ID 0x%08lx 0x%08lx 0x%08lx 0x%08lx\r\n"
|
|
,_fx_,pListenCallbackParams->ConferenceID.buffer[0],
|
|
pListenCallbackParams->ConferenceID.buffer[4],
|
|
pListenCallbackParams->ConferenceID.buffer[8],
|
|
pListenCallbackParams->ConferenceID.buffer[12]));
|
|
ERRORMESSAGE(("%s:Received 0x%08lx in state %d, accepting object not found\r\n"
|
|
,_fx_,hStatus, m_Phase));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
// non error case falls out
|
|
switch(pListenCallbackParams->wGoal)
|
|
{
|
|
default:
|
|
case CC_GOAL_UNKNOWN:
|
|
break;
|
|
|
|
case CC_GOAL_CREATE:
|
|
case CC_GOAL_JOIN:
|
|
case CC_GOAL_INVITE:
|
|
m_ConferenceID = pListenCallbackParams->ConferenceID;
|
|
m_hCall = pListenCallbackParams->hCall;
|
|
|
|
if(pListenCallbackParams->pCallerAliasNames || pListenCallbackParams->pszDisplay)
|
|
{
|
|
NewRemoteUserInfo(pListenCallbackParams->pCallerAliasNames,
|
|
pListenCallbackParams->pszDisplay);
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s:null pListenCallbackParams->pCallerAliasNames\r\n",_fx_));
|
|
}
|
|
|
|
if(!OnCallAccept(pListenCallbackParams))
|
|
{
|
|
ERRORMESSAGE(("ListenCallback:OnCallAccept failed\r\n"));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Main conference indication dispatcher
|
|
//
|
|
#ifdef DEBUG
|
|
TCHAR *i_strs[ ] =
|
|
{
|
|
"ERROR! - INDICATION ZERO",
|
|
"CC_RINGING_INDICATION",
|
|
"CC_CONNECT_INDICATION",
|
|
"CC_TX_CHANNEL_OPEN_INDICATION",
|
|
"CC_RX_CHANNEL_REQUEST_INDICATION",
|
|
"CC_RX_CHANNEL_CLOSE_INDICATION",
|
|
"CC_MUTE_INDICATION",
|
|
"CC_UNMUTE_INDICATION",
|
|
"CC_PEER_ADD_INDICATION",
|
|
"CC_PEER_DROP_INDICATION",
|
|
"CC_PEER_CHANGE_CAP_INDICATION",
|
|
"CC_CONFERENCE_TERMINATION_INDICATION",
|
|
"CC_HANGUP_INDICATION",
|
|
"CC_RX_NONSTANDARD_MESSAGE_INDICATION",
|
|
"CC_MULTIPOINT_INDICATION",
|
|
"CC_PEER_UPDATE_INDICATION",
|
|
"CC_H245_MISCELLANEOUS_COMMAND_INDICATION",
|
|
"CC_H245_MISCELLANEOUS_INDICATION_INDICATION",
|
|
"CC_H245_CONFERENCE_REQUEST_INDICATION",
|
|
"CC_H245_CONFERENCE_RESPONSE_INDICATION",
|
|
"CC_H245_CONFERENCE_COMMAND_INDICATION",
|
|
"CC_H245_CONFERENCE_INDICATION_INDICATION",
|
|
"CC_FLOW_CONTROL_INDICATION",
|
|
"CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION",
|
|
"CC_REQUEST_MODE_INDICATION",
|
|
"CC_REQUEST_MODE_RESPONSE_INDICATION",
|
|
"CC_VENDOR_ID_INDICATION",
|
|
"CC_MAXIMUM_AUDIO_VIDEO_SKEW_INDICATION",
|
|
"CC_T120_CHANNEL_REQUEST_INDICATION",
|
|
"CC_T120_CHANNEL_OPEN_INDICATION",
|
|
"CC_BANDWIDTH_CHANGED_INDICATION",
|
|
"CC_ACCEPT_CHANNEL_INDICATION",
|
|
"CC_TERMINAL_ID_REQUEST_INDICATION",
|
|
"CC_PING_RESPONSE_INDICATION",
|
|
"CC_TERMINAL_NUMBER_INDICATION"
|
|
};
|
|
#endif //DEBUG
|
|
|
|
HRESULT CH323Ctrl::ConfCallback (BYTE bIndication,
|
|
HRESULT hStatus, PCC_CONFERENCE_CALLBACK_PARAMS pConferenceCallbackParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::ConfCallback");
|
|
HRESULT hr = CC_NOT_IMPLEMENTED;
|
|
DEBUGMSG(ZONE_CONN,("%s: %s\r\n", _fx_, i_strs[bIndication]));
|
|
|
|
SHOW_OBJ_ETIME(i_strs[bIndication]);
|
|
|
|
switch (bIndication)
|
|
{
|
|
case CC_RINGING_INDICATION:
|
|
// (PCC_RINGING_CALLBACK_PARAMS) pConferenceCallbackParams;
|
|
// user info may be available now and it may not be
|
|
OnCallRinging(hStatus, (PCC_RINGING_CALLBACK_PARAMS) pConferenceCallbackParams);
|
|
|
|
break;
|
|
case CC_CONNECT_INDICATION:
|
|
OnCallConnect(hStatus, (PCC_CONNECT_CALLBACK_PARAMS) pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_PEER_ADD_INDICATION:
|
|
case CC_PEER_UPDATE_INDICATION:
|
|
case CC_PEER_DROP_INDICATION:
|
|
case CC_TERMINAL_NUMBER_INDICATION:
|
|
break;
|
|
|
|
case CC_HANGUP_INDICATION:
|
|
OnHangup(hStatus);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_CONFERENCE_TERMINATION_INDICATION:
|
|
// September 1996 comments:
|
|
// I don't know if there will also be a CC_HANGUP_INDICATION after this.
|
|
// We're going to call Hangup() via Disconnect()
|
|
// December 1996: Hangup() (excuse me, CC_Hangup()) no longer gives back a
|
|
// CC_HANGUP_INDICATION in this state. It returns an error. The new behavior
|
|
// seems to indicate that the call control channel is already dead at this point
|
|
// so, set our flags as such!!!
|
|
m_ChanFlags &= ~(CTRLF_OPEN);
|
|
//set state to indicate disconnecting.
|
|
GoNextPhase(CCS_Disconnecting);
|
|
DoAdvise(CCEV_REMOTE_DISCONNECTING ,NULL);
|
|
GoNextPhase(CCS_Idle); // no need to ck retval - we're disconnected
|
|
// notify the UI or application code or whatever..
|
|
DoAdvise(CCEV_DISCONNECTED ,NULL);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_PEER_CHANGE_CAP_INDICATION:
|
|
break;
|
|
|
|
//
|
|
// Channel stuff
|
|
//
|
|
case CC_TX_CHANNEL_OPEN_INDICATION:
|
|
OnChannelOpen(hStatus,(PCC_TX_CHANNEL_OPEN_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_RX_CHANNEL_REQUEST_INDICATION:
|
|
OnChannelRequest(hStatus, (PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
break;
|
|
|
|
// the following 4 channel-centric indications have the same basic parameter
|
|
// structure. When we get the final Intel drop, we can clean it up. 1 - collapse
|
|
// the parameters into a common "channel indication" structure. 2 - make sure
|
|
// that a user pointer is stored in that structure for easy finding of channel
|
|
// context. 3 - collapse separate channel event handling functions into one.
|
|
case CC_MUTE_INDICATION:
|
|
OnMute(hStatus, (PCC_MUTE_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_UNMUTE_INDICATION:
|
|
OnUnMute(hStatus, (PCC_UNMUTE_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_RX_CHANNEL_CLOSE_INDICATION:
|
|
OnRxChannelClose(hStatus,(PCC_RX_CHANNEL_CLOSE_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
break;
|
|
case CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION:
|
|
OnTxChannelClose(hStatus,(PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
hr = CC_OK;
|
|
// CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION callback parameters (pConferenceCallbackParams)
|
|
//typedef struct {
|
|
// CC_HCHANNEL hChannel;
|
|
//} CC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS, *PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS;
|
|
break;
|
|
case CC_FLOW_CONTROL_INDICATION:
|
|
// CC_FLOW_CONTROL_INDICATION callback parameters (pConferenceCallbackParams)
|
|
// typedef struct {
|
|
// CC_HCHANNEL hChannel;
|
|
// DWORD dwRate;
|
|
// } CC_FLOW_CONTROL_CALLBACK_PARAMS, *PCC_FLOW_CONTROL_CALLBACK_PARAMS;
|
|
break;
|
|
|
|
case CC_BANDWIDTH_CHANGED_INDICATION:
|
|
case CC_REQUEST_MODE_INDICATION:
|
|
case CC_REQUEST_MODE_RESPONSE_INDICATION:
|
|
break;
|
|
|
|
case CC_ACCEPT_CHANNEL_INDICATION:
|
|
hr = CC_OK;
|
|
OnChannelAcceptComplete(hStatus, (PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
break;
|
|
//
|
|
// Misc commands and indications. Some are related to channels
|
|
//
|
|
case CC_RX_NONSTANDARD_MESSAGE_INDICATION:
|
|
break;
|
|
case CC_H245_MISCELLANEOUS_COMMAND_INDICATION:
|
|
OnMiscCommand(hStatus,
|
|
(PCC_H245_MISCELLANEOUS_COMMAND_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
break;
|
|
case CC_H245_MISCELLANEOUS_INDICATION_INDICATION: // from the Department of Redundancy Department
|
|
OnMiscIndication(hStatus,
|
|
(PCC_H245_MISCELLANEOUS_INDICATION_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
break;
|
|
case CC_T120_CHANNEL_REQUEST_INDICATION:
|
|
OnT120ChannelRequest(hStatus,(PCC_T120_CHANNEL_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
break;
|
|
case CC_T120_CHANNEL_OPEN_INDICATION:
|
|
OnT120ChannelOpen(hStatus,(PCC_T120_CHANNEL_OPEN_CALLBACK_PARAMS)pConferenceCallbackParams);
|
|
default:
|
|
break;
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
VOID CH323Ctrl::OnT120ChannelRequest(
|
|
HRESULT hStatus,
|
|
PCC_T120_CHANNEL_REQUEST_CALLBACK_PARAMS pT120RequestParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnT120ChannelRequest");
|
|
PSOCKADDR_IN pAddr;
|
|
SOCKADDR_IN sinD;
|
|
CC_ADDR ChannelAddr;
|
|
PCC_ADDR pChannelAddr;
|
|
GUID mediaID;
|
|
DWORD dwRejectReason = H245_REJ;
|
|
BOOL bFound = FALSE;
|
|
POSITION pos = m_ChannelList.GetHeadPosition();
|
|
ICtrlCommChan *pChannel = NULL;
|
|
|
|
// look for a matching channel instance.
|
|
while (pos)
|
|
{
|
|
pChannel = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
|
|
ASSERT(pChannel);
|
|
|
|
hrLast = pChannel->GetMediaType(&mediaID);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
goto ERROR_EXIT;
|
|
if(mediaID == MEDIA_TYPE_H323_T120)
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!HR_SUCCEEDED(hrLast) || !bFound)
|
|
{
|
|
// Non-default channels Not Yet Implemented!!!!
|
|
// When it is, ask the parent conference object to create another channel of the
|
|
// specified media type.
|
|
if(hrLast == CCO_E_NODEFAULT_CHANNEL)
|
|
dwRejectReason = H245_REJ_TYPE_NOTAVAIL;
|
|
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
|
|
// if we are the H.245 master and have requested a T.120 channel already,
|
|
// reject this request.
|
|
if(m_ConferenceAttributes.bMaster && pChannel->GetHChannel())
|
|
{
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
if(!pChannel->IsChannelEnabled()) // allow this channel ?
|
|
{
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
|
|
pChannel->SetHChannel(pT120RequestParams->hChannel);
|
|
if(pT120RequestParams->pAddr)
|
|
{
|
|
// the other end is listening on the specified address
|
|
sinD.sin_family = AF_INET;
|
|
sinD.sin_addr.S_un.S_addr = htonl(pT120RequestParams->pAddr->Addr.IP_Binary.dwAddr);
|
|
sinD.sin_port = htons(pT120RequestParams->pAddr->Addr.IP_Binary.wPort);
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s, requestor listening on port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
pT120RequestParams->pAddr->Addr.IP_Binary.wPort,
|
|
pT120RequestParams->pAddr->Addr.IP_Binary.dwAddr));
|
|
|
|
hrLast = pChannel->AcceptRemoteAddress(&sinD);
|
|
pChannelAddr = NULL;
|
|
}
|
|
else
|
|
{
|
|
// the channel selects its local address(es)/port(s)
|
|
if(!pChannel->SelectPorts((LPIControlChannel)this))
|
|
{
|
|
ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
|
|
hrLast = CCO_E_BAD_ADDRESS;
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
// get the address and ports of our end of the channel
|
|
pAddr = pChannel->GetLocalAddress();
|
|
// fixup channel addr pair structure.
|
|
ChannelAddr.nAddrType = CC_IP_BINARY;
|
|
ChannelAddr.bMulticast = FALSE;
|
|
ChannelAddr.Addr.IP_Binary.wPort = ntohs(pAddr->sin_port);
|
|
ChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
|
|
pChannelAddr = &ChannelAddr;
|
|
DEBUGMSG(ZONE_CONN,("%s: accepting on port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
ChannelAddr.Addr.IP_Binary.wPort,ChannelAddr.Addr.IP_Binary.dwAddr));
|
|
}
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest accepting");
|
|
|
|
hrLast = CC_AcceptT120Channel(
|
|
pChannel->GetHChannel(),
|
|
FALSE, // BOOL bAssociateConference,
|
|
NULL, // PCC_OCTETSTRING pExternalReference,
|
|
pChannelAddr);
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, CC_AcceptT120Channel returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto ERROR_EXIT;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest accepted");
|
|
|
|
// LOOKLOOK !!! the 2 following lines would not be there because we should
|
|
// Wait for CC_ACCEPT_CHANNEL_INDICATION. But the CC_ACCEPT_CHANNEL_INDICATION
|
|
// is missing if a send audio and send video channel is open at the time this
|
|
// channel is accepted. A bug in CALLCONT.DLL that needs investigating.
|
|
hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN);
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest, open done");
|
|
|
|
// ******
|
|
// LOOKLOOK if OnChannelOpen returns an error, need to close the channel
|
|
// but pChannel->Close() is not yet implemented for bidirectional channels
|
|
// ******
|
|
|
|
m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_BIDI, pChannel, this);
|
|
//
|
|
// Check for readiness to notify that all required channels are open
|
|
//
|
|
CheckChannelsReady( ); //
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest done");
|
|
|
|
return;
|
|
|
|
REJECT_CHANNEL:
|
|
{
|
|
// need private HRESULT! don't overwrite the reason we're rejecting the channel!!
|
|
HRESULT hr;
|
|
ERRORMESSAGE(("%s, rejecting channel\r\n",_fx_));
|
|
|
|
hr = CC_RejectChannel(pT120RequestParams->hChannel, dwRejectReason);
|
|
if(hr != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, CC_RejectChannel returned 0x%08lX\r\n",_fx_, hr));
|
|
}
|
|
}
|
|
ERROR_EXIT:
|
|
return;
|
|
}
|
|
|
|
VOID CH323Ctrl::OnT120ChannelOpen(
|
|
HRESULT hStatus,
|
|
PCC_T120_CHANNEL_OPEN_CALLBACK_PARAMS pT120OpenParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnT120ChannelOpen");
|
|
SOCKADDR_IN sinD;
|
|
GUID mediaID;
|
|
ICtrlCommChan *pChannel = (ICtrlCommChan *)pT120OpenParams->dwUserToken;
|
|
// validate channel token - is this what we think it is?
|
|
if(IsBadWritePtr(pChannel, sizeof(ICtrlCommChan)))
|
|
{
|
|
ERRORMESSAGE(("%s:invalid channel token: 0x%08lx\r\n",_fx_, pT120OpenParams->dwUserToken));
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
POSITION pos = m_ChannelList.GetHeadPosition();
|
|
ICtrlCommChan *pChan;
|
|
BOOL bValid = FALSE;
|
|
// look for a matching channel instance.
|
|
while (pos)
|
|
{
|
|
pChan = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
|
|
ASSERT(pChan);
|
|
if(pChan == pChannel)
|
|
{
|
|
bValid = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if(!bValid)
|
|
{
|
|
ERRORMESSAGE(("%s:unrecognized token 0x%08lX\r\n",_fx_,
|
|
pT120OpenParams->dwUserToken));
|
|
return;
|
|
}
|
|
#endif //DEBUG
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen");
|
|
|
|
if(hStatus != CC_OK)
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s: hStatus:0x%08lX\r\n",_fx_,hStatus));
|
|
// LOOKLOOK need to interpret hStatus
|
|
// let the channel know what happened.
|
|
|
|
// if the request was rejected due to a collision of T.120 O.L.C. requests,
|
|
// (other end is the master and other end also requested a T.120 channel)
|
|
// then proceed with the call.
|
|
|
|
if(m_ConferenceAttributes.bMaster)
|
|
{
|
|
// the slave would only reject in a real error condition
|
|
pChannel->OnChannelOpen(CHANNEL_REJECTED);
|
|
// the channel knows what happened, so let it do the worrying.
|
|
return;
|
|
|
|
}
|
|
else // just a typical collision
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
// if the other end specified its listen address, use it
|
|
if(pT120OpenParams->pAddr)
|
|
{
|
|
if(pT120OpenParams->pAddr->nAddrType != CC_IP_BINARY)
|
|
{
|
|
ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,
|
|
pT120OpenParams->pAddr->nAddrType));
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
// we now have the remote port info ( in host byte order)
|
|
sinD.sin_family = AF_INET;
|
|
sinD.sin_addr.S_un.S_addr = htonl(pT120OpenParams->pAddr->Addr.IP_Binary.dwAddr);
|
|
sinD.sin_port = htons(pT120OpenParams->pAddr->Addr.IP_Binary.wPort);
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s, opened on port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
pT120OpenParams->pAddr->Addr.IP_Binary.wPort,pT120OpenParams->pAddr->Addr.IP_Binary.dwAddr));
|
|
|
|
hrLast = pChannel->AcceptRemoteAddress(&sinD);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:AcceptRemoteAddress failed\r\n",_fx_));
|
|
goto ERROR_EXIT;
|
|
}
|
|
}
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen opening");
|
|
|
|
hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:channel's OnChannelOpen() returned 0x%08lX\r\n", _fx_, hrLast));
|
|
CloseChannel(pChannel);
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen open done");
|
|
|
|
m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_BIDI, pChannel, this);
|
|
|
|
//
|
|
// Check for readiness to notify that all required channels are open
|
|
//
|
|
CheckChannelsReady( );
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen done");
|
|
return;
|
|
|
|
ERROR_EXIT:
|
|
// need to cleanup, disconnect, etc.
|
|
m_hCallCompleteCode = CCCI_CHANNEL_OPEN_ERROR;
|
|
// let the parent Conference object know about the imminent disconnect
|
|
DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
|
|
hrLast = CCO_E_MANDATORY_CHAN_OPEN_FAILED;
|
|
|
|
InternalDisconnect();
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// This once did something. Currently, it's called whenever a channel is opened. The
|
|
// call to GoNextPhase(CCS_Ready) changes state and posts a notification upward, but
|
|
// that notification is currently ignored. (it's useless)
|
|
// Reminder to mikev: A new notification is needed to indicate that capabilities
|
|
// have been exchanged and it is OK to open channels.
|
|
//
|
|
VOID CH323Ctrl::CheckChannelsReady()
|
|
{
|
|
GoNextPhase(CCS_Ready);
|
|
}
|
|
// handles local hangup indication
|
|
VOID CH323Ctrl::OnHangup(HRESULT hStatus)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnHangup");
|
|
DEBUGMSG(ZONE_CONN,("%s:CC_HANGUP_INDICATION in phase %d\r\n", _fx_, m_Phase));
|
|
switch(m_Phase)
|
|
{
|
|
case CCS_Disconnecting:
|
|
GoNextPhase(CCS_Idle);
|
|
Cleanup();
|
|
DoAdvise(CCEV_DISCONNECTED ,NULL);
|
|
break;
|
|
|
|
default: // do nothing
|
|
ERRORMESSAGE(("%s:Unexpected CC_HANGUP_INDICATION\r\n",_fx_));
|
|
break;
|
|
}
|
|
}
|
|
|
|
HRESULT CH323Ctrl::CloseChannel(ICtrlCommChan* pChannel)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::CloseChannel");
|
|
if(!pChannel->IsChannelOpen())
|
|
{
|
|
ERRORMESSAGE(("%s: channel is not open\r\n",_fx_));
|
|
hrLast = CCO_E_INVALID_PARAM;
|
|
goto EXIT;
|
|
}
|
|
|
|
hrLast = CC_CloseChannel(pChannel->GetHChannel());
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s: CC_CloseChannel returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto EXIT;
|
|
}
|
|
// make the channel handle its own media stream specific shutdown and cleanup chores
|
|
hrLast = pChannel->OnChannelClose(CHANNEL_CLOSED);
|
|
|
|
EXIT:
|
|
return hrLast;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::AddChannel(ICtrlCommChan * pCommChannel, LPIH323PubCap pCapabilityResolver)
|
|
{
|
|
ICtrlCommChan *pChan = NULL;
|
|
|
|
|
|
// get the ICtrlCommChannel interface of each channel
|
|
hrLast = pCommChannel->QueryInterface(IID_ICtrlCommChannel,(void **)&pChan);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
goto ADD_ERROR;
|
|
|
|
// make the channel aware of its new scope
|
|
hrLast = pChan->BeginControlSession(this, pCapabilityResolver);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
goto ADD_ERROR;
|
|
// add it to the list
|
|
m_ChannelList.AddTail(pChan);
|
|
return hrSuccess;
|
|
|
|
ADD_ERROR:
|
|
if(pChan)
|
|
pChan->Release();
|
|
return CHAN_E_INVALID_PARAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CH323Ctrl::OpenChannel(ICtrlCommChan* pChan, IH323PubCap *pCapResolver,
|
|
MEDIA_FORMAT_ID dwIDLocalSend, MEDIA_FORMAT_ID dwIDRemoteRecv)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OpenChannel");
|
|
CC_TERMCAP H245ChannelCap;
|
|
PSOCKADDR_IN pAddr;
|
|
CC_ADDR ChannelAddr;
|
|
LPVOID pChannelParams;
|
|
PCC_TERMCAP pSaveChannelCapability = NULL;
|
|
UINT uLocalParamSize;
|
|
BYTE SessionID;
|
|
BYTE payload_type;
|
|
DWORD_PTR dwhChannel;
|
|
GUID mediaID;
|
|
|
|
ASSERT((pChan->IsChannelOpen()== FALSE) && (pChan->IsOpenPending()== FALSE));
|
|
hrLast = pChan->GetMediaType(&mediaID);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
goto CHANNEL_ERROR;
|
|
|
|
if (mediaID == MEDIA_TYPE_H323_T120)
|
|
{
|
|
if(pChan->GetHChannel()) // already accepted a T.120 channel?
|
|
{
|
|
ERRORMESSAGE(("%s, already have a pending channel\r\n",_fx_));
|
|
goto CHANNEL_ERROR; // this is not an error, excuse the label
|
|
}
|
|
|
|
// test the no common capability case. notify the conference object of the
|
|
// inability to open the channel, and return success
|
|
|
|
if(dwIDLocalSend == INVALID_MEDIA_FORMAT)
|
|
{
|
|
pChan->OnChannelOpen(CHANNEL_NO_CAPABILITY);
|
|
return hrSuccess;
|
|
}
|
|
// There is no "standard" rule regarding which end specifies the "listen"
|
|
// address of a T.120 channel. However: we want NetMeeting-NetMeeting calls
|
|
// to behave consistently (the "caller" always "places the T.120 call").
|
|
// Therefore, specify the address if this end is not the originator. That will
|
|
// force the other end to specify it's address.
|
|
|
|
if(IsOriginating(m_ChanFlags))
|
|
{
|
|
pAddr = NULL; // the other end "listens" and we "connect"
|
|
}
|
|
else // listen on local address
|
|
{
|
|
// select ports if they are not already selected
|
|
if(!pChan->SelectPorts((LPIControlChannel)this))
|
|
{
|
|
ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
|
|
hrLast = CCO_E_BAD_ADDRESS;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
|
|
// get the address and port
|
|
pAddr = pChan->GetLocalAddress();
|
|
// fixup channel addr structure.
|
|
ChannelAddr.nAddrType = CC_IP_BINARY;
|
|
ChannelAddr.bMulticast = FALSE;
|
|
ChannelAddr.Addr.IP_Binary.wPort = ntohs(pAddr->sin_port);
|
|
ChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
|
|
}
|
|
|
|
hrLast = CC_OpenT120Channel(
|
|
// CC_HCONFERENCE hConference,
|
|
m_hConference,
|
|
// PCC_HCHANNEL phChannel,
|
|
&dwhChannel,
|
|
// BOOL bAssociateConference,
|
|
FALSE,
|
|
// PCC_OCTETSTRING pExternalReference,
|
|
NULL,
|
|
// PCC_ADDR pAddr,
|
|
IsOriginating(m_ChanFlags) ? NULL : &ChannelAddr,
|
|
// DWORD dwChannelBitRate,
|
|
0,
|
|
// DWORD dwUserToken);
|
|
(DWORD_PTR)pChan);
|
|
|
|
// and fall out to test hrLast, etc.
|
|
}
|
|
else // is an audio or video channel
|
|
{
|
|
// test the no common capability case. If the channel is mandatory,
|
|
// return an error, else notify the conference object of the
|
|
// inability to open the channel, and return success
|
|
|
|
if((dwIDLocalSend == INVALID_MEDIA_FORMAT) ||(dwIDRemoteRecv == INVALID_MEDIA_FORMAT))
|
|
{
|
|
pChan->OnChannelOpen(CHANNEL_NO_CAPABILITY);
|
|
return hrSuccess;
|
|
}
|
|
|
|
//
|
|
// test if we need to try to open this !!!
|
|
//
|
|
if(!pChan->IsChannelEnabled())
|
|
{
|
|
return hrSuccess;
|
|
}
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel");
|
|
|
|
// Get the remote channel parameters for
|
|
// the send channel - these parameters are used to request a channel
|
|
uLocalParamSize = pCapResolver->GetLocalSendParamSize((MEDIA_FORMAT_ID)dwIDLocalSend);
|
|
pChannelParams=MemAlloc (uLocalParamSize);
|
|
if (pChannelParams == NULL) {
|
|
//Doom
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
hrLast = pCapResolver->GetEncodeParams(
|
|
(LPVOID)&H245ChannelCap, sizeof(H245ChannelCap),
|
|
(LPVOID)pChannelParams, uLocalParamSize,
|
|
(AUDIO_FORMAT_ID)dwIDRemoteRecv,
|
|
(AUDIO_FORMAT_ID)dwIDLocalSend);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s: GetEncodeParams returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
|
|
// set session ID and payload type. Note that payload type is relevant only for
|
|
// dynamic payloads. Otherwise, it must be zero.
|
|
if (H245ChannelCap.DataType == H245_DATA_AUDIO)
|
|
{
|
|
payload_type = ((PAUDIO_CHANNEL_PARAMETERS)pChannelParams)->RTP_Payload;
|
|
// Session ID is 1 for Audio, 2 for Video . H245 7.3.1 (H2250 Logical Channel Param)
|
|
SessionID=1;
|
|
}
|
|
else if (H245ChannelCap.DataType == H245_DATA_VIDEO)
|
|
{
|
|
payload_type = ((PVIDEO_CHANNEL_PARAMETERS)pChannelParams)->RTP_Payload;
|
|
SessionID=2;
|
|
}
|
|
// payload_type must be zero for fixed payload types. Weird.
|
|
if(!IsDynamicPayload(payload_type))
|
|
payload_type = 0;
|
|
|
|
// create a marshalled version of channel parameters and store it in the channel
|
|
// for later reference
|
|
if(H245ChannelCap.ClientType == H245_CLIENT_AUD_NONSTD)
|
|
{
|
|
// Make a flat copy of the nonstandard capability to store as the channel
|
|
// parameter
|
|
UINT uSize = H245ChannelCap.Cap.H245Aud_NONSTD.data.length;
|
|
pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) + uSize);
|
|
if(!pSaveChannelCapability)
|
|
{
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
// copy fixed part
|
|
memcpy(pSaveChannelCapability, &H245ChannelCap, sizeof(CC_TERMCAP));
|
|
// variable part follows the fixed part
|
|
pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value
|
|
= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
|
|
// copy variable part
|
|
memcpy(pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value,
|
|
H245ChannelCap.Cap.H245Aud_NONSTD.data.value,
|
|
H245ChannelCap.Cap.H245Aud_NONSTD.data.length);
|
|
// and length
|
|
pSaveChannelCapability->Cap.H245Aud_NONSTD.data.length
|
|
= H245ChannelCap.Cap.H245Aud_NONSTD.data.length;
|
|
|
|
// make the channel remember the channel parameters.
|
|
// a zero size as the second arg means that a preallocated chunk is being passed
|
|
hrLast = pChan->ConfigureCapability(pSaveChannelCapability, 0,
|
|
pChannelParams, uLocalParamSize);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:ConfigureCapability returned 0x%08lx\r\n",_fx_, hrLast));
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
pSaveChannelCapability=NULL; // the channel owns this memory now
|
|
}
|
|
else if(H245ChannelCap.ClientType == H245_CLIENT_VID_NONSTD)
|
|
{
|
|
// Make a flat copy of the nonstandard capability to store as the channel
|
|
// parameter
|
|
UINT uSize = H245ChannelCap.Cap.H245Vid_NONSTD.data.length;
|
|
pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) + uSize);
|
|
if(!pSaveChannelCapability)
|
|
{
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
// copy fixed part
|
|
memcpy(pSaveChannelCapability, &H245ChannelCap, sizeof(CC_TERMCAP));
|
|
// variable part follows the fixed part
|
|
pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value
|
|
= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
|
|
// copy variable part
|
|
memcpy(pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value,
|
|
H245ChannelCap.Cap.H245Vid_NONSTD.data.value,
|
|
H245ChannelCap.Cap.H245Vid_NONSTD.data.length);
|
|
// and length
|
|
pSaveChannelCapability->Cap.H245Vid_NONSTD.data.length
|
|
= H245ChannelCap.Cap.H245Vid_NONSTD.data.length;
|
|
|
|
// make the channel remember the channel parameters.
|
|
// a zero size as the second arg means that a preallocated chunk is being passed
|
|
hrLast = pChan->ConfigureCapability(pSaveChannelCapability, 0,
|
|
pChannelParams, uLocalParamSize);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:ConfigureCapability returned 0x%08lx\r\n",_fx_, hrLast));
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
pSaveChannelCapability=NULL; // the channel owns this memory now
|
|
}
|
|
else
|
|
{
|
|
// only need to remember the already-flat H.245 cap structure.
|
|
hrLast = pChan->ConfigureCapability(&H245ChannelCap, sizeof(CC_TERMCAP),
|
|
pChannelParams, uLocalParamSize);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:ConfigureCapability returned 0x%08lx\r\n",_fx_, hrLast));
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
}
|
|
|
|
// remember both versions of the resolved send format for the channel
|
|
// we're about to open
|
|
pChan->SetNegotiatedLocalFormat(dwIDLocalSend);
|
|
pChan->SetNegotiatedRemoteFormat(dwIDRemoteRecv);
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel done configuring");
|
|
|
|
// select ports if they are not already selected
|
|
if(!pChan->SelectPorts((LPIControlChannel)this))
|
|
{
|
|
ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
|
|
hrLast = CCO_E_BAD_ADDRESS;
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
|
|
// get the address and port of our RTCP channel
|
|
pAddr = pChan->GetLocalAddress();
|
|
// fixup channel addr structure. There are two ports, but in RTP, it is implicit
|
|
// that the RTCP control port is the next highest port number.
|
|
// The open logical channel request needs the reverse RTCP port to be specified.
|
|
ChannelAddr.nAddrType = CC_IP_BINARY;
|
|
ChannelAddr.bMulticast = FALSE;
|
|
ChannelAddr.Addr.IP_Binary.wPort = pChan->GetLocalRTCPPort();
|
|
ChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s: opening using RTCP port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
ChannelAddr.Addr.IP_Binary.wPort,ChannelAddr.Addr.IP_Binary.dwAddr));
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s: requesting capability ID:0x%08lX\r\n",
|
|
_fx_, H245ChannelCap.CapId));
|
|
|
|
// open a channel
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel, opening");
|
|
|
|
hrLast = CC_OpenChannel(m_hConference, &dwhChannel,
|
|
SessionID,
|
|
0, // BYTE bAssociatedSessionID,
|
|
TRUE, //BOOL bSilenceSuppression, WE ALWAYS DO SILENCE SUPPRESSION
|
|
&H245ChannelCap,
|
|
&ChannelAddr, // the local address on which we're listening for RTCP
|
|
payload_type, // PAYLOAD TYPE
|
|
0, // DWORD dwChannelBitRate,
|
|
(DWORD_PTR)pChan); // use the channel pointer as the user token
|
|
} // end else is an audio or video channel
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s: OpenChannel returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto CHANNEL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pChan->SetHChannel(dwhChannel);
|
|
pChan->OnChannelOpening();
|
|
}
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel done");
|
|
return hrLast;
|
|
|
|
CHANNEL_ERROR:
|
|
if(pSaveChannelCapability)
|
|
MemFree(pSaveChannelCapability);
|
|
|
|
return hrLast;
|
|
}
|
|
|
|
|
|
VOID CH323Ctrl::CleanupConferenceAttributes()
|
|
{
|
|
WORD w;
|
|
if(m_ConferenceAttributes.pParticipantList->ParticipantInfoArray)
|
|
{
|
|
for(w=0;w<m_ConferenceAttributes.pParticipantList->wLength;w++)
|
|
{
|
|
if(m_ConferenceAttributes.pParticipantList->
|
|
ParticipantInfoArray[w].TerminalID.pOctetString)
|
|
{
|
|
MemFree(m_ConferenceAttributes.pParticipantList->
|
|
ParticipantInfoArray[w].TerminalID.pOctetString);
|
|
}
|
|
|
|
}
|
|
|
|
MemFree(m_ConferenceAttributes.pParticipantList->ParticipantInfoArray);
|
|
}
|
|
m_ConferenceAttributes.pParticipantList->ParticipantInfoArray = NULL;
|
|
m_ConferenceAttributes.pParticipantList->wLength = 0;
|
|
|
|
}
|
|
|
|
HRESULT CH323Ctrl::AllocConferenceAttributes()
|
|
{
|
|
WORD w;
|
|
#define MAX_PART_LEN 128
|
|
if(m_ConferenceAttributes.pParticipantList->wLength)
|
|
{
|
|
m_ConferenceAttributes.pParticipantList->ParticipantInfoArray =
|
|
(PCC_PARTICIPANTINFO) MemAlloc (sizeof(CC_PARTICIPANTINFO)
|
|
* m_ConferenceAttributes.pParticipantList->wLength);
|
|
|
|
if(!m_ConferenceAttributes.pParticipantList->ParticipantInfoArray)
|
|
return CCO_E_OUT_OF_MEMORY;
|
|
|
|
for(w=0;w<m_ConferenceAttributes.pParticipantList->wLength;w++)
|
|
{
|
|
m_ConferenceAttributes.pParticipantList->
|
|
ParticipantInfoArray[w].TerminalID.pOctetString
|
|
= (BYTE *)MemAlloc(MAX_PART_LEN);
|
|
if(m_ConferenceAttributes.pParticipantList->
|
|
ParticipantInfoArray[w].TerminalID.pOctetString)
|
|
{
|
|
m_ConferenceAttributes.pParticipantList->
|
|
ParticipantInfoArray[w].TerminalID.wOctetStringLength
|
|
= MAX_PART_LEN;
|
|
}
|
|
else
|
|
{
|
|
m_ConferenceAttributes.pParticipantList->
|
|
ParticipantInfoArray[w].TerminalID.wOctetStringLength =0;
|
|
return CCO_E_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
return hrSuccess;
|
|
}
|
|
|
|
VOID CH323Ctrl::OnCallConnect(HRESULT hStatus, PCC_CONNECT_CALLBACK_PARAMS pConfParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnCallConnect");
|
|
PCC_TERMCAPLIST pTermCapList;
|
|
PCC_TERMCAPDESCRIPTORS pTermCapDescriptors;
|
|
CC_TERMCAP H245ChannelCap;
|
|
PCC_TERMCAP pChannelCap = NULL;
|
|
CapsCtl *pCapabilityResolver = NULL;
|
|
GUID mediaID;
|
|
POSITION pos = NULL;
|
|
ICtrlCommChan *pChan = NULL;
|
|
|
|
if(hStatus != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s hStatus=0x%08lx in phase %d\r\n",_fx_,hStatus,m_Phase));
|
|
|
|
// test for gatekeeper admission reject
|
|
// FACILITY_GKIADMISSION
|
|
if(CUSTOM_FACILITY(hStatus) == FACILITY_GKIADMISSION)
|
|
{
|
|
// pass this code intact - do not remap
|
|
m_hCallCompleteCode = hStatus;
|
|
}
|
|
else
|
|
{
|
|
switch (hStatus)
|
|
{
|
|
default:
|
|
// reason is unknown
|
|
m_hCallCompleteCode = CCCI_UNKNOWN;
|
|
break;
|
|
case CC_PEER_REJECT:
|
|
if(m_Phase == CCS_Connecting)
|
|
{
|
|
switch(pConfParams->bRejectReason)
|
|
{
|
|
case CC_REJECT_ADAPTIVE_BUSY:
|
|
case CC_REJECT_IN_CONF:
|
|
case CC_REJECT_USER_BUSY:
|
|
m_hCallCompleteCode = CCCI_BUSY;
|
|
break;
|
|
case CC_REJECT_SECURITY_DENIED:
|
|
m_hCallCompleteCode = CCCI_SECURITY_DENIED;
|
|
break;
|
|
case CC_REJECT_NO_ANSWER:
|
|
case CC_REJECT_TIMER_EXPIRED:
|
|
m_hCallCompleteCode = CCCI_NO_ANSWER_TIMEOUT;
|
|
break;
|
|
case CC_REJECT_GATEKEEPER_RESOURCES:
|
|
m_hCallCompleteCode = CCCI_GK_NO_RESOURCES;
|
|
break;
|
|
default:
|
|
//#define CC_REJECT_NO_BANDWIDTH 1
|
|
//#define CC_REJECT_GATEKEEPER_RESOURCES 2
|
|
//#define CC_REJECT_UNREACHABLE_DESTINATION 3
|
|
//#define CC_REJECT_DESTINATION_REJECTION 4
|
|
//#define CC_REJECT_INVALID_REVISION 5
|
|
//#define CC_REJECT_NO_PERMISSION 6
|
|
//#define CC_REJECT_UNREACHABLE_GATEKEEPER 7
|
|
//#define CC_REJECT_GATEWAY_RESOURCES 8
|
|
//#define CC_REJECT_BAD_FORMAT_ADDRESS 9
|
|
//#define CC_REJECT_ROUTE_TO_GATEKEEPER 12
|
|
// would be nice to handle this -->> //#define CC_REJECT_CALL_FORWARDED 13
|
|
//#define CC_REJECT_ROUTE_TO_MC 14
|
|
//#define CC_REJECT_UNDEFINED_REASON 15
|
|
//#define CC_REJECT_INTERNAL_ERROR 16 // Internal error occured in peer CS stack.
|
|
//#define CC_REJECT_NORMAL_CALL_CLEARING 17 // Normal call hangup
|
|
//#define CC_REJECT_NOT_IMPLEMENTED 20 // Service has not been implemented
|
|
//#define CC_REJECT_MANDATORY_IE_MISSING 21 // Pdu missing mandatory ie
|
|
//#define CC_REJECT_INVALID_IE_CONTENTS 22 // Pdu ie was incorrect
|
|
//#define CC_REJECT_CALL_DEFLECTION 24 // You deflected the call, so lets quit.
|
|
//#define CC_REJECT_GATEKEEPER_TERMINATED 25 // Gatekeeper terminated call
|
|
|
|
m_hCallCompleteCode = CCCI_REJECTED;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s:Received CC_PEER_REJECT in state %d\r\n",_fx_,m_Phase));
|
|
}
|
|
break;
|
|
case CC_INTERNAL_ERROR:
|
|
m_hCallCompleteCode = CCCI_LOCAL_ERROR;
|
|
break;
|
|
|
|
}
|
|
}
|
|
// let the parent Conference object know (unless this is the answering end)
|
|
if(m_Phase == CCS_Connecting)
|
|
{
|
|
DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
|
|
}
|
|
|
|
InternalDisconnect();
|
|
return;
|
|
}
|
|
else if(!pConfParams)
|
|
{
|
|
ERRORMESSAGE(("OnCallConnect: null pConfParams\r\n"));
|
|
m_hCallCompleteCode = CCCI_LOCAL_ERROR;
|
|
DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
|
|
InternalDisconnect();
|
|
return;
|
|
}
|
|
|
|
SetRemoteVendorID(pConfParams->pVendorInfo);
|
|
|
|
GoNextPhase(CCS_Opening);
|
|
m_ChanFlags |= (CTRLF_OPEN);
|
|
DEBUGMSG(ZONE_CONN,("%s:CONNECTION_CONNECTED\r\n", _fx_));
|
|
if((!pConfParams->pLocalAddr) || (pConfParams->pLocalAddr->nAddrType != CC_IP_BINARY))
|
|
{
|
|
if(pConfParams->pLocalAddr)
|
|
{
|
|
ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,pConfParams->pLocalAddr->nAddrType));
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s: null local address\r\n",_fx_));
|
|
}
|
|
|
|
ERRORMESSAGE(("%s:where's the local address????\r\n",_fx_));
|
|
PHOSTENT phe;
|
|
PSOCKADDR_IN psin;
|
|
char szTemp[200];
|
|
LPCSTR lpHostName;
|
|
gethostname(szTemp,sizeof(szTemp));
|
|
lpHostName = szTemp;
|
|
psin = &local_sin;
|
|
phe = gethostbyname(lpHostName);
|
|
if (phe != NULL)
|
|
{
|
|
memcpy((char FAR *)&(psin->sin_addr), phe->h_addr,phe->h_length);
|
|
psin->sin_family = AF_INET;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// remember our local address
|
|
local_sin.sin_family = AF_INET;
|
|
// in host byte order
|
|
local_sin.sin_addr.S_un.S_addr = htonl(pConfParams->pLocalAddr->Addr.IP_Binary.dwAddr);
|
|
// in host byte order
|
|
local_sin.sin_port = htons(pConfParams->pLocalAddr->Addr.IP_Binary.wPort);
|
|
}
|
|
DEBUGMSG(ZONE_CONN,("%s local port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
local_sin.sin_port,local_sin.sin_addr.S_un.S_addr));
|
|
|
|
// get remote address
|
|
if((!pConfParams->pPeerAddr) || (pConfParams->pPeerAddr->nAddrType != CC_IP_BINARY))
|
|
{
|
|
if(pConfParams->pPeerAddr)
|
|
{
|
|
ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,pConfParams->pPeerAddr->nAddrType));
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s: null local address\r\n",_fx_));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// remember the remote peer address
|
|
remote_sin.sin_family = AF_INET;
|
|
// in host byte order
|
|
remote_sin.sin_addr.S_un.S_addr = htonl(pConfParams->pPeerAddr->Addr.IP_Binary.dwAddr);
|
|
// in host byte order
|
|
remote_sin.sin_port = htons(pConfParams->pPeerAddr->Addr.IP_Binary.wPort);
|
|
}
|
|
//
|
|
// The only available remote user information in this state is the Q.931 display name.
|
|
// If we are the callee, we got the caller alias name (wire format was unicode) in
|
|
// the listen callback parameters. If we are the caller, we really need the callee
|
|
// alias name(s), which are not propagated. Fallback to the Q.931 display name (ASCII)
|
|
//
|
|
|
|
NewRemoteUserInfo(NULL, pConfParams->pszPeerDisplay);
|
|
|
|
// release any stale memory, reset ConferenceAttributes struture
|
|
CleanupConferenceAttributes();
|
|
// get the number of conference participants etc.
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect getting attribs 1");
|
|
|
|
hrLast = CC_GetConferenceAttributes(m_hConference, &m_ConferenceAttributes);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{// fatal error
|
|
ERRORMESSAGE(("%s,CC_GetConferenceAttributes returned 0x%08lX\r\n", _fx_, hrLast));
|
|
goto CONNECT_ERROR;
|
|
|
|
}
|
|
hrLast = AllocConferenceAttributes();
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{// fatal error
|
|
ERRORMESSAGE(("%s,AllocConferenceAttributes returned 0x%08lX\r\n", _fx_, hrLast));
|
|
goto CONNECT_ERROR;
|
|
|
|
}
|
|
// now get the real attributes
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect getting attribs 2");
|
|
hrLast = CC_GetConferenceAttributes(m_hConference, &m_ConferenceAttributes);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{// fatal error
|
|
ERRORMESSAGE(("%s,CC_GetConferenceAttributes returned 0x%08lX\r\n", _fx_, hrLast));
|
|
goto CONNECT_ERROR;
|
|
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect got attribs");
|
|
|
|
m_ConferenceID =m_ConferenceAttributes.ConferenceID;
|
|
m_bMultipointController = m_ConferenceAttributes.bMultipointController;
|
|
|
|
hrLast = m_pConfAdvise->GetCapResolver((LPVOID *)&pCapabilityResolver, OID_CAP_ACM_TO_H323);
|
|
if(!HR_SUCCEEDED(hrLast) || (pCapabilityResolver == NULL))
|
|
{// fatal error
|
|
ERRORMESSAGE(("%s,null resolver\r\n", _fx_));
|
|
goto CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
// get the remote capabilities
|
|
// cache the remote capabilities now
|
|
pTermCapList = pConfParams->pTermCapList;
|
|
pTermCapDescriptors = pConfParams->pTermCapDescriptors;
|
|
hrLast = pCapabilityResolver->AddRemoteDecodeCaps(pTermCapList, pTermCapDescriptors, &m_RemoteVendorInfo);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{// fatal error
|
|
ERRORMESSAGE(("%s,AddRemoteDecodeCaps returned 0x%08lX\r\n", _fx_, hrLast));
|
|
goto CONNECT_ERROR;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect saved caps");
|
|
DoAdvise(CCEV_CAPABILITIES_READY, NULL); // put connobj in a state to allow other
|
|
// channels to be added & opened
|
|
//
|
|
// notify UI here. It wants remote user info.
|
|
//
|
|
ConnectNotify(CCEV_CONNECTED);
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect notified");
|
|
return;
|
|
|
|
CONNECT_ERROR:
|
|
// release all channels
|
|
ReleaseAllChannels();
|
|
InternalDisconnect();
|
|
}
|
|
|
|
// LOOKLOOK methinks ConnectNotify might need to propagate the conference ID.
|
|
// This will be a moot point if we have a real property interface. Watch
|
|
// for this in the meantime
|
|
VOID CH323Ctrl::ConnectNotify(DWORD dwEvent)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::ConnectNotify");
|
|
CTRL_USER_INFO UserInfo;
|
|
LPWSTR lpwstr = NULL;
|
|
WCHAR wc =0;
|
|
|
|
// init to zero in case of error
|
|
UserInfo.dwCallerIDSize = 0;
|
|
UserInfo.lpvCallerIDData = NULL;
|
|
UserInfo.lpvRemoteProtocolInfo = NULL;
|
|
UserInfo.lpvLocalProtocolInfo = NULL;
|
|
|
|
// alias address strings, e.g. caller ID, are in UNICODE
|
|
if( m_pRemoteAliasItem &&
|
|
m_pRemoteAliasItem->pData &&
|
|
*((LPWSTR*)(m_pRemoteAliasItem->pData)))
|
|
{
|
|
lpwstr =(LPWSTR)m_pRemoteAliasItem->pData;
|
|
}
|
|
else
|
|
{
|
|
lpwstr = pwszPeerDisplayName;
|
|
}
|
|
|
|
if(lpwstr)
|
|
{
|
|
if(pwszPeerAliasName)
|
|
{
|
|
MemFree(pwszPeerAliasName);
|
|
}
|
|
ULONG ulSize = (lstrlenW(lpwstr) + 1) * sizeof(WCHAR);
|
|
pwszPeerAliasName = (LPWSTR)MemAlloc(ulSize);
|
|
LStrCpyW(pwszPeerAliasName, lpwstr);
|
|
// point to user name stuff
|
|
UserInfo.dwCallerIDSize = ulSize;
|
|
UserInfo.lpvCallerIDData = (LPVOID)pwszPeerAliasName;
|
|
}
|
|
else
|
|
{
|
|
// point to the single NULL character on the stack
|
|
UserInfo.dwCallerIDSize = 1;
|
|
UserInfo.lpvCallerIDData = &wc;
|
|
}
|
|
DoAdvise(dwEvent, &UserInfo);
|
|
}
|
|
|
|
|
|
|
|
VOID CH323Ctrl::NewRemoteUserInfo(PCC_ALIASNAMES pRemoteAliasNames,
|
|
LPWSTR pwszRemotePeerDisplayName)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::NewRemoteUserInfo");
|
|
ULONG ulSize;
|
|
PCC_ALIASITEM pItem;
|
|
WORD wC;
|
|
// make a copy of the user display name (what else???) We need to hold this
|
|
// at least until the parent object is notified and has a chance to copy the
|
|
// information
|
|
|
|
// Future implementation will store each item as a distinct property.
|
|
// These will be accessable via the IProperty interface
|
|
|
|
// find the display name if it exists
|
|
if(pRemoteAliasNames)
|
|
{
|
|
wC = pRemoteAliasNames->wCount;
|
|
pItem = pRemoteAliasNames->pItems;
|
|
while (wC--)
|
|
{
|
|
if(!pItem)
|
|
{
|
|
continue;
|
|
}
|
|
if(pItem->wType == CC_ALIAS_H323_ID)
|
|
{
|
|
if(!pItem->wDataLength || !pItem->pData)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(m_pRemoteAliasItem)
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s: Releasing previous user info\r\n",_fx_));
|
|
MemFree(m_pRemoteAliasItem);
|
|
}
|
|
// The H323 ID is UNICODE, and needs to be converted to ANSI
|
|
// for propagation to UI/client app. The conversion is done
|
|
// in ConnectNotify()
|
|
|
|
// need enough mem for the struct, the name, + null terminator
|
|
ulSize = ((pItem->wDataLength +1)*sizeof(WCHAR)) + sizeof(CC_ALIASITEM);
|
|
|
|
m_pRemoteAliasItem = (PCC_ALIASITEM)MemAlloc(ulSize);
|
|
memcpy(m_pRemoteAliasItem, pItem, sizeof(CC_ALIASITEM));
|
|
m_pRemoteAliasItem->pData = (WCHAR*)(((char *)m_pRemoteAliasItem)+sizeof(CC_ALIASITEM));
|
|
memcpy(m_pRemoteAliasItem->pData, pItem->pData, pItem->wDataLength*sizeof(WCHAR));
|
|
// need to null terminate it
|
|
*(WCHAR *)(((BYTE *)m_pRemoteAliasItem->pData) + pItem->wDataLength*sizeof(WCHAR))
|
|
= (WCHAR)0;
|
|
}
|
|
pItem++;
|
|
}
|
|
}
|
|
if(pwszRemotePeerDisplayName)
|
|
{
|
|
if(pwszPeerDisplayName)
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s: Releasing previous pwszPeerDisplayName\r\n",_fx_));
|
|
MemFree(pwszPeerDisplayName);
|
|
}
|
|
// this WAS the Q.931 display name which WAS always ascii
|
|
// ulSize = lstrlen(szRemotePeerDisplayName) + 1;
|
|
// Now it's unicode
|
|
ulSize = (lstrlenW(pwszRemotePeerDisplayName) + 1)* sizeof(WCHAR);
|
|
pwszPeerDisplayName = (LPWSTR)MemAlloc(ulSize);
|
|
memcpy(pwszPeerDisplayName, pwszRemotePeerDisplayName, ulSize);
|
|
}
|
|
}
|
|
|
|
VOID CH323Ctrl::OnCallRinging(HRESULT hStatus, PCC_RINGING_CALLBACK_PARAMS pRingingParams)
|
|
{
|
|
if(pRingingParams->pNonStandardData)
|
|
{
|
|
|
|
// nyi
|
|
}
|
|
DoAdvise(CCEV_RINGING, NULL);
|
|
}
|
|
|
|
|
|
|
|
HRESULT CH323Ctrl::FindDefaultRXChannel(PCC_TERMCAP pChannelCapability, ICtrlCommChan **lplpChannel)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::FindDefaultRXChannel");
|
|
HRESULT hr = hrSuccess;
|
|
GUID mediaID;
|
|
POSITION pos = m_ChannelList.GetHeadPosition();
|
|
ICtrlCommChan *pChannel;
|
|
if(!pChannelCapability | !lplpChannel)
|
|
{
|
|
ERRORMESSAGE(("%s: null param:pcap:0x%08lX, pchan:0x%08lX\r\n",_fx_,
|
|
pChannelCapability, lplpChannel));
|
|
hr = CCO_E_INVALID_PARAM;
|
|
goto EXIT;
|
|
}
|
|
|
|
// look for a matching channel instance.
|
|
while (pos)
|
|
{
|
|
pChannel = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
|
|
ASSERT(pChannel);
|
|
if(pChannel->IsSendChannel() == FALSE)
|
|
{
|
|
hr = pChannel->GetMediaType(&mediaID);
|
|
if(!HR_SUCCEEDED(hr))
|
|
goto EXIT;
|
|
if(((mediaID == MEDIA_TYPE_H323AUDIO) && (pChannelCapability->DataType ==H245_DATA_AUDIO))
|
|
|| ((mediaID == MEDIA_TYPE_H323VIDEO) && (pChannelCapability->DataType ==H245_DATA_VIDEO)))
|
|
{
|
|
*lplpChannel = pChannel;
|
|
return hrSuccess;
|
|
}
|
|
}
|
|
}
|
|
// fallout if not found
|
|
hr = CCO_E_NODEFAULT_CHANNEL;
|
|
EXIT:
|
|
return hr;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
VOID DumpWFX(LPWAVEFORMATEX lpwfxLocal, LPWAVEFORMATEX lpwfxRemote)
|
|
{
|
|
FX_ENTRY("DumpWFX");
|
|
ERRORMESSAGE((" -------- %s Begin --------\r\n",_fx_));
|
|
if(lpwfxLocal)
|
|
{
|
|
ERRORMESSAGE((" -------- Local --------\r\n"));
|
|
ERRORMESSAGE(("wFormatTag:\t0x%04X, nChannels:\t0x%04X\r\n",
|
|
lpwfxLocal->wFormatTag, lpwfxLocal->nChannels));
|
|
ERRORMESSAGE(("nSamplesPerSec:\t0x%08lX, nAvgBytesPerSec:\t0x%08lX\r\n",
|
|
lpwfxLocal->nSamplesPerSec, lpwfxLocal->nAvgBytesPerSec));
|
|
ERRORMESSAGE(("nBlockAlign:\t0x%04X, wBitsPerSample:\t0x%04X, cbSize:\t0x%04X\r\n",
|
|
lpwfxLocal->nBlockAlign, lpwfxLocal->wBitsPerSample, lpwfxLocal->cbSize));
|
|
}
|
|
if(lpwfxRemote)
|
|
{
|
|
ERRORMESSAGE((" -------- Remote --------\r\n"));
|
|
ERRORMESSAGE(("wFormatTag:\t0x%04X, nChannels:\t0x%04X\r\n",
|
|
lpwfxRemote->wFormatTag, lpwfxRemote->nChannels));
|
|
ERRORMESSAGE(("nSamplesPerSec:\t0x%08lX, nAvgBytesPerSec:\t0x%08lX\r\n",
|
|
lpwfxRemote->nSamplesPerSec, lpwfxRemote->nAvgBytesPerSec));
|
|
ERRORMESSAGE(("nBlockAlign:\t0x%04X, wBitsPerSample:\t0x%04X, cbSize:\t0x%04X\r\n",
|
|
lpwfxRemote->nBlockAlign, lpwfxRemote->wBitsPerSample, lpwfxRemote->cbSize));
|
|
}
|
|
ERRORMESSAGE((" -------- %s End --------\r\n",_fx_));
|
|
}
|
|
VOID DumpChannelParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2)
|
|
{
|
|
FX_ENTRY("DumpChannelParameters");
|
|
ERRORMESSAGE((" -------- %s Begin --------\r\n",_fx_));
|
|
if(pChanCap1)
|
|
{
|
|
ERRORMESSAGE((" -------- Local Cap --------\r\n"));
|
|
ERRORMESSAGE(("DataType:%d(d), ClientType:%d(d)\r\n",pChanCap1->DataType,pChanCap1->ClientType));
|
|
ERRORMESSAGE(("Direction:%d(d), CapId:%d(d)\r\n",pChanCap1->Dir,pChanCap1->CapId));
|
|
}
|
|
if(pChanCap2)
|
|
{
|
|
ERRORMESSAGE((" -------- Remote Cap --------\r\n"));
|
|
ERRORMESSAGE(("DataType:%d(d), ClientType:%d(d)\r\n",pChanCap2->DataType,pChanCap2->ClientType));
|
|
ERRORMESSAGE(("Direction:%d(d), CapId:%d(d)\r\n",pChanCap2->Dir,pChanCap2->CapId));
|
|
}
|
|
ERRORMESSAGE((" -------- %s End --------\r\n",_fx_));
|
|
}
|
|
VOID DumpNonstdParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2)
|
|
{
|
|
FX_ENTRY("DumpNonstdParameters");
|
|
|
|
ERRORMESSAGE((" -------- %s Begin --------\r\n",_fx_));
|
|
DumpChannelParameters(pChanCap1, pChanCap2);
|
|
|
|
if(pChanCap1)
|
|
{
|
|
ERRORMESSAGE((" -------- Local Cap --------\r\n"));
|
|
if(pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice == h221NonStandard_chosen)
|
|
{
|
|
ERRORMESSAGE(("t35CountryCode:%d(d), t35Extension:%d(d)\r\n",
|
|
pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode,
|
|
pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension));
|
|
ERRORMESSAGE(("MfrCode:%d(d), data length:%d(d)\r\n",
|
|
pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode,
|
|
pChanCap1->Cap.H245Aud_NONSTD.data.length));
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("unrecognized nonStandardIdentifier.choice: %d(d)\r\n",
|
|
pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice));
|
|
}
|
|
}
|
|
if(pChanCap2)
|
|
{
|
|
ERRORMESSAGE((" -------- Remote Cap --------\r\n"));
|
|
if(pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice == h221NonStandard_chosen)
|
|
{
|
|
ERRORMESSAGE(("t35CountryCode:%d(d), t35Extension:%d(d)\r\n",
|
|
pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode,
|
|
pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension));
|
|
ERRORMESSAGE(("MfrCode:%d(d), data length:%d(d)\r\n",
|
|
pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode,
|
|
pChanCap2->Cap.H245Aud_NONSTD.data.length));
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("nonStandardIdentifier.choice: %d(d)\r\n",
|
|
pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice));
|
|
}
|
|
}
|
|
ERRORMESSAGE((" -------- %s End --------\r\n",_fx_));
|
|
}
|
|
#else
|
|
#define DumpWFX(x,y)
|
|
#define DumpChannelParameters(x,y)
|
|
#define DumpNonstdParameters(x,y)
|
|
#endif
|
|
|
|
// make sure requested channel parameters are valid (data type, ID and capability
|
|
// structure are consistent). Also obtains the local channel parameters needed
|
|
// to deal with the resulting stream
|
|
//
|
|
BOOL CH323Ctrl::ValidateChannelParameters(PCC_TERMCAP pChanCapLocal, PCC_TERMCAP pChanCapRemote)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::ValidateChannelParameters");
|
|
if((pChanCapLocal->DataType != pChanCapRemote->DataType)
|
|
|| (pChanCapLocal->ClientType != pChanCapRemote->ClientType))
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s:unmatched type\r\n",_fx_));
|
|
DumpChannelParameters(pChanCapLocal, pChanCapRemote);
|
|
return FALSE;
|
|
}
|
|
if(pChanCapLocal->ClientType == H245_CLIENT_AUD_NONSTD)
|
|
{
|
|
PNSC_AUDIO_CAPABILITY pNSCapLocal, pNSCapRemote;
|
|
|
|
if((pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice
|
|
!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice )
|
|
||(pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode
|
|
!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode)
|
|
||(pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension
|
|
!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension)
|
|
||(pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode
|
|
!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode)
|
|
||(pChanCapLocal->Cap.H245Aud_NONSTD.data.length
|
|
!= pChanCapRemote->Cap.H245Aud_NONSTD.data.length))
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s:unmatched NonStd capability\r\n",_fx_));
|
|
DumpNonstdParameters(pChanCapLocal, pChanCapRemote);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
pNSCapLocal = (PNSC_AUDIO_CAPABILITY)pChanCapLocal->Cap.H245Aud_NONSTD.data.value;
|
|
pNSCapRemote = (PNSC_AUDIO_CAPABILITY)pChanCapRemote->Cap.H245Aud_NONSTD.data.value;
|
|
|
|
// we only know about NSC_ACM_WAVEFORMATEX at this time
|
|
if(pNSCapRemote->cap_type != NSC_ACM_WAVEFORMATEX)
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s:unrecognized NonStd capability type %d\r\n",_fx_, pNSCapRemote->cap_type));
|
|
return FALSE;
|
|
}
|
|
if((pNSCapLocal->cap_data.wfx.cbSize != pNSCapRemote->cap_data.wfx.cbSize)
|
|
|| (memcmp(&pNSCapLocal->cap_data.wfx, &pNSCapRemote->cap_data.wfx, sizeof(WAVEFORMATEX)) != 0))
|
|
{
|
|
DumpWFX(&pNSCapLocal->cap_data.wfx, &pNSCapRemote->cap_data.wfx);
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
// if it falls out, it's valid
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
BOOL CH323Ctrl::ConfigureRecvChannelCapability(
|
|
ICtrlCommChan *pChannel,
|
|
PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS pChannelParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::ConfigureRecvChannelCapability");
|
|
//IH323PubCap *pCapObject = NULL;
|
|
CapsCtl *pCapObject = NULL;
|
|
// CCapability *pCapObject = NULL;
|
|
DWORD dwFormatID =INVALID_AUDIO_FORMAT;
|
|
PCC_TERMCAP pChannelCapability = pChannelParams->pChannelCapability, pSaveChannelCapability = NULL;
|
|
UINT uSize, uLocalParamSize;
|
|
LPVOID lpvData;
|
|
LPVOID pLocalParams;
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s: requested capability ID:0x%08lX, dir %d, type %d\r\n",
|
|
_fx_, pChannelCapability->CapId, pChannelCapability->Dir,
|
|
pChannelCapability->DataType));
|
|
|
|
|
|
// at one time, we thought the capability ID would be valid
|
|
// and we would be receiving the format specified in pChannelCapability->CapId
|
|
// but it IS NOT VALID. The only viable info is in the channel parameters.
|
|
// The code would be --->>> dwFormatID = pChannelCapability->CapId;
|
|
|
|
// the ID *should* be all that is necessary to configure ourselves.
|
|
// However.....
|
|
|
|
// validate media (data) type - why? shouldn't this be prevalidated?
|
|
// shouldn't this be eventually used to select a channel object from
|
|
// among multiple channel objects?
|
|
if((pChannelCapability->DataType != H245_DATA_AUDIO) && (pChannelCapability->DataType != H245_DATA_VIDEO))
|
|
{
|
|
hrLast = CCO_E_UNSUPPORTED_MEDIA_TYPE;
|
|
DumpChannelParameters(NULL, pChannelCapability);
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
|
|
// Look at the local capability referenced by pChannelCapability->CapId
|
|
// and Validate the format details
|
|
|
|
hrLast = m_pConfAdvise->GetCapResolver((LPVOID *)&pCapObject, OID_CAP_ACM_TO_H323);
|
|
if(!HR_SUCCEEDED(hrLast) || (pCapObject == NULL))
|
|
{
|
|
ERRORMESSAGE(("%s: null resolver\r\n",_fx_));
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
|
|
// Find the local *receive* capability that matches the remote *send* channel
|
|
// parameters and get the local parameters.
|
|
|
|
uLocalParamSize = pCapObject->GetLocalRecvParamSize(pChannelCapability);
|
|
pLocalParams=MemAlloc (uLocalParamSize);
|
|
if (pLocalParams == NULL)
|
|
{
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
hrLast = ((CapsCtl *)pCapObject)->GetDecodeParams( pChannelParams,
|
|
(MEDIA_FORMAT_ID *)&dwFormatID, pLocalParams, uLocalParamSize);
|
|
|
|
if(!HR_SUCCEEDED(hrLast) || (dwFormatID == INVALID_AUDIO_FORMAT))
|
|
{
|
|
ERRORMESSAGE(("%s:GetDecodeParams returned 0x%08lx\r\n",_fx_, hrLast));
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
|
|
|
|
// create a marshalled version of channel parameters and store it in the channel for later
|
|
// reference
|
|
if(pChannelCapability->ClientType == H245_CLIENT_AUD_NONSTD)
|
|
{
|
|
// The nonstandard capability already passed all the recognition tests so
|
|
// don't need to test again.
|
|
// Make a flat copy of the nonstandard capability
|
|
uSize = pChannelCapability->Cap.H245Aud_NONSTD.data.length;
|
|
// lpData = pChannelCapability->Cap.H245Aud_NONSTD.data.value;
|
|
|
|
pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) + uSize);
|
|
if(!pSaveChannelCapability)
|
|
{
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
// copy fixed part
|
|
memcpy(pSaveChannelCapability, pChannelCapability, sizeof(CC_TERMCAP));
|
|
// variable part follows the fixed part
|
|
pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value
|
|
= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
|
|
// copy variable part
|
|
memcpy(pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value,
|
|
pChannelCapability->Cap.H245Aud_NONSTD.data.value,
|
|
pChannelCapability->Cap.H245Aud_NONSTD.data.length);
|
|
// and length
|
|
pSaveChannelCapability->Cap.H245Aud_NONSTD.data.length
|
|
= pChannelCapability->Cap.H245Aud_NONSTD.data.length;
|
|
|
|
// make the channel remember the channel parameters.
|
|
// a zero size as the second arg means that a preallocated chunk is being passed
|
|
hrLast = pChannel->ConfigureCapability(pSaveChannelCapability, 0,
|
|
pLocalParams, uLocalParamSize);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:ConfigureCapability (recv) returned 0x%08lx\r\n",_fx_, hrLast));
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
pSaveChannelCapability=NULL; // the channel owns this memory now
|
|
}
|
|
else if(pChannelCapability->ClientType == H245_CLIENT_VID_NONSTD)
|
|
{
|
|
// The nonstandard capability already passed all the recognition tests so
|
|
// don't need to test again.
|
|
// Make a flat copy of the nonstandard capability
|
|
uSize = pChannelCapability->Cap.H245Vid_NONSTD.data.length;
|
|
// lpData = pChannelCapability->Cap.H245Vid_NONSTD.data.value;
|
|
|
|
pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) + uSize);
|
|
if(!pSaveChannelCapability)
|
|
{
|
|
hrLast = CCO_E_SYSTEM_ERROR;
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
// copy fixed part
|
|
memcpy(pSaveChannelCapability, pChannelCapability, sizeof(CC_TERMCAP));
|
|
// variable part follows the fixed part
|
|
pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value
|
|
= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
|
|
// copy variable part
|
|
memcpy(pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value,
|
|
pChannelCapability->Cap.H245Vid_NONSTD.data.value,
|
|
pChannelCapability->Cap.H245Vid_NONSTD.data.length);
|
|
// and length
|
|
pSaveChannelCapability->Cap.H245Vid_NONSTD.data.length
|
|
= pChannelCapability->Cap.H245Vid_NONSTD.data.length;
|
|
|
|
// make the channel remember the channel parameters.
|
|
// a zero size as the second arg means that a preallocated chunk is being passed
|
|
hrLast = pChannel->ConfigureCapability(pSaveChannelCapability, 0,
|
|
pLocalParams, uLocalParamSize);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:ConfigureCapability (recv) returned 0x%08lx\r\n",_fx_, hrLast));
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
pSaveChannelCapability=NULL; // the channel owns this memory now
|
|
}
|
|
else
|
|
{
|
|
// only need to remember the already-flat H.245 cap structure.
|
|
hrLast = pChannel->ConfigureCapability(pChannelCapability, sizeof(CC_TERMCAP),
|
|
pLocalParams, uLocalParamSize);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s:ConfigureCapability(recv) returned 0x%08lx\r\n",_fx_, hrLast));
|
|
goto BAD_CAPABILITY_EXIT;
|
|
}
|
|
}
|
|
// Remember the receive format ID
|
|
pChannel->SetNegotiatedLocalFormat(dwFormatID);
|
|
|
|
// very special case check for video Temporal/Spatial tradeoff capability.
|
|
// Set the property of the channel accordingly
|
|
if(pChannelCapability->DataType == H245_DATA_VIDEO )
|
|
{
|
|
BOOL bTSCap;
|
|
bTSCap = ((PVIDEO_CHANNEL_PARAMETERS)pLocalParams)->TS_Tradeoff;
|
|
pChannel->CtrlChanSetProperty(PROP_REMOTE_TS_CAPABLE,&bTSCap, sizeof(bTSCap));
|
|
// don't bother checking or panicking over this SetProperty error
|
|
}
|
|
return TRUE;
|
|
|
|
///////////////////
|
|
BAD_CAPABILITY_EXIT:
|
|
ERRORMESSAGE(("%s:received bad capability\r\n",_fx_));
|
|
hrLast = CCO_E_INVALID_CAPABILITY;
|
|
if(pSaveChannelCapability)
|
|
MemFree(pSaveChannelCapability);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// we're being requested to open a channel for receive
|
|
//
|
|
VOID CH323Ctrl::OnChannelRequest(HRESULT hStatus,
|
|
PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS pChannelReqParams)
|
|
{
|
|
FX_ENTRY("CH323Ctrl::OnChannelRequest");
|
|
|
|
CC_ADDR CChannelAddr, DChannelAddr;
|
|
PCC_ADDR pCChannelAddr = pChannelReqParams->pPeerRTCPAddr;;
|
|
PCC_TERMCAP pChannelCapability;
|
|
PSOCKADDR_IN pAddr;
|
|
SOCKADDR_IN sinC;
|
|
pChannelCapability = pChannelReqParams->pChannelCapability;
|
|
DWORD dwRejectReason = H245_REJ;
|
|
ICtrlCommChan *pChannel;
|
|
|
|
if(!pChannelCapability)
|
|
{
|
|
ERRORMESSAGE(("OnChannelRequest: null capability\r\n"));
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest");
|
|
|
|
//
|
|
// Try to find a default channel to handle this open request.
|
|
hrLast = FindDefaultRXChannel(pChannelCapability, &pChannel);
|
|
if(!HR_SUCCEEDED(hrLast) || !pChannel)
|
|
{
|
|
// Non-default channels Not Yet Implemented!!!!
|
|
// Ask the parent conference object to create another channel of the
|
|
// specified media type. The H.245 media type should map to one of the
|
|
// media type GUIDs that the parent conference object understands.
|
|
// GUID typeGuid;
|
|
// if(!MapGuidType(pChannelCapability, &typeGUID))
|
|
// goto REJECT_CHANNEL;
|
|
// hrLast = m_pConfAdvise->GetChannel(&typeGuid, &pChannel);
|
|
// if(!HR_SUCCEEDED(hrLast))
|
|
// goto REJECT_CHANNEL;
|
|
if(hrLast == CCO_E_NODEFAULT_CHANNEL)
|
|
dwRejectReason = H245_REJ_TYPE_NOTAVAIL;
|
|
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
|
|
if(pChannel->GetHChannel())
|
|
{
|
|
ERRORMESSAGE(("%s: existing channel or leak:0x%08lX\r\n",_fx_,
|
|
pChannel->GetHChannel()));
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
|
|
//
|
|
// test if we want to allow this !!!
|
|
//
|
|
if(!pChannel->IsChannelEnabled())
|
|
{
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
|
|
pChannel->SetHChannel(pChannelReqParams->hChannel);
|
|
|
|
// configure based on the requested capability. (store capability ID, validate requested
|
|
// capabilities
|
|
if(!ConfigureRecvChannelCapability(pChannel, pChannelReqParams))
|
|
{
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest done configuring");
|
|
|
|
// select our receive ports for this RTP session
|
|
|
|
if(!pChannel->SelectPorts((LPIControlChannel)this))
|
|
{
|
|
ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
|
|
hrLast = CCO_E_BAD_ADDRESS;
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
|
|
if(pCChannelAddr)
|
|
{
|
|
if(pCChannelAddr->nAddrType != CC_IP_BINARY)
|
|
{
|
|
ERRORMESSAGE(("%s:invalid address type %d\r\n",_fx_, pCChannelAddr->nAddrType));
|
|
hrLast = CCO_E_BAD_ADDRESS;
|
|
goto REJECT_CHANNEL;
|
|
}
|
|
// pass the remote RTCP address to the channel instance
|
|
sinC.sin_family = AF_INET;
|
|
sinC.sin_addr.S_un.S_addr = htonl(pCChannelAddr->Addr.IP_Binary.dwAddr);
|
|
sinC.sin_port = htons(pCChannelAddr->Addr.IP_Binary.wPort);
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s, request reverse port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
pCChannelAddr->Addr.IP_Binary.wPort,pCChannelAddr->Addr.IP_Binary.dwAddr));
|
|
|
|
hrLast = pChannel->AcceptRemoteRTCPAddress(&sinC);
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, AcceptRemoteRTCPAddress returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto ERROR_EXIT;
|
|
}
|
|
}
|
|
|
|
// get the address and ports of our end of the channel
|
|
pAddr = pChannel->GetLocalAddress();
|
|
// fixup channel addr pair structure.
|
|
DChannelAddr.nAddrType = CC_IP_BINARY;
|
|
DChannelAddr.bMulticast = FALSE;
|
|
DChannelAddr.Addr.IP_Binary.wPort = pChannel->GetLocalRTPPort();
|
|
DChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
|
|
|
|
CChannelAddr.nAddrType = CC_IP_BINARY;
|
|
CChannelAddr.bMulticast = FALSE;
|
|
CChannelAddr.Addr.IP_Binary.wPort = pChannel->GetLocalRTCPPort();
|
|
CChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s: accepting on port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
DChannelAddr.Addr.IP_Binary.wPort,DChannelAddr.Addr.IP_Binary.dwAddr));
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest accepting");
|
|
|
|
hrLast = CC_AcceptChannel(pChannelReqParams->hChannel,&DChannelAddr, &CChannelAddr,
|
|
0 /* this param is the bitrate that will be used by THIS channel !! */);
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, CC_AcceptChannel returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto ERROR_EXIT;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest accepted");
|
|
return;
|
|
|
|
REJECT_CHANNEL:
|
|
{
|
|
// need private HRESULT! don't overwrite the reason we're rejecting the channel!!
|
|
HRESULT hr;
|
|
|
|
if(NULL != pChannelCapability)
|
|
{
|
|
ERRORMESSAGE(("%s, rejecting channel, Dir:%d, DataType:%d, ClientType:%d, CapId:%d\r\n",
|
|
_fx_, pChannelCapability->Dir, pChannelCapability->DataType,
|
|
pChannelCapability->ClientType, pChannelCapability->CapId));
|
|
}
|
|
|
|
hr = CC_RejectChannel(pChannelReqParams->hChannel, dwRejectReason);
|
|
if(hr != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, CC_RejectChannel returned 0x%08lX\r\n",_fx_, hr));
|
|
}
|
|
}
|
|
ERROR_EXIT:
|
|
return;
|
|
}
|
|
|
|
VOID CH323Ctrl::OnChannelAcceptComplete(HRESULT hStatus,
|
|
PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS pChannelParams)
|
|
{
|
|
FX_ENTRY("CH323Ctrl::OnChannelAcceptComplete");
|
|
ICtrlCommChan *pChannel;
|
|
if(hStatus != CC_OK)
|
|
{
|
|
return;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelAcceptComplete");
|
|
|
|
pChannel = FindChannel(pChannelParams->hChannel);
|
|
if(!pChannel)
|
|
{
|
|
ERRORMESSAGE(("OnChannelAcceptComplete: hChannel 0x%08lx not found\r\n", pChannelParams->hChannel));
|
|
return;
|
|
}
|
|
|
|
hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN); // the receive side is open
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelAcceptComplete, open done");
|
|
if(HR_SUCCEEDED(hrLast))
|
|
{
|
|
m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_RX, pChannel, this);
|
|
}
|
|
//
|
|
// Check for readiness to notify that all required channels are open
|
|
//
|
|
CheckChannelsReady( );
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelAcceptComplete done");
|
|
}
|
|
|
|
VOID CH323Ctrl::OnChannelOpen(HRESULT hStatus,
|
|
PCC_TX_CHANNEL_OPEN_CALLBACK_PARAMS pChannelParams )
|
|
{
|
|
FX_ENTRY("CH323Ctrl::OnChannelOpen");
|
|
PCC_ADDR pChannelRTPAddr;
|
|
PCC_ADDR pChannelRTCPAddr;
|
|
SOCKADDR_IN sinC, sinD;
|
|
|
|
ICtrlCommChan *pChannel = (ICtrlCommChan *)pChannelParams->dwUserToken;
|
|
// validate channel token - is this what we think it is?
|
|
if(IsBadWritePtr(pChannel, sizeof(ICtrlCommChan)))
|
|
{
|
|
ERRORMESSAGE(("%s:invalid channel token: 0x%08lx\r\n",_fx_, pChannelParams->dwUserToken));
|
|
return;
|
|
}
|
|
if(pChannel->IsSendChannel() == FALSE)
|
|
{
|
|
ERRORMESSAGE(("%s:not a send channel:token 0x%08lX\r\n",_fx_,
|
|
pChannelParams->dwUserToken));
|
|
return;
|
|
}
|
|
#ifdef DEBUG
|
|
POSITION pos = m_ChannelList.GetHeadPosition();
|
|
ICtrlCommChan *pChan;
|
|
BOOL bValid = FALSE;
|
|
// look for a matching channel instance.
|
|
while (pos)
|
|
{
|
|
pChan = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
|
|
ASSERT(pChan);
|
|
if(pChan == pChannel)
|
|
{
|
|
bValid = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if(!bValid)
|
|
{
|
|
ERRORMESSAGE(("%s:unrecognized token 0x%08lX\r\n",_fx_,
|
|
pChannelParams->dwUserToken));
|
|
return;
|
|
}
|
|
#endif //DEBUG
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen");
|
|
|
|
if((hStatus != CC_OK) || (!(pChannelRTPAddr = pChannelParams->pPeerRTPAddr))
|
|
|| (!(pChannelRTCPAddr = pChannelParams->pPeerRTCPAddr)))
|
|
{
|
|
ERRORMESSAGE(("%s: hStatus:0x%08lX, address:0x%08lX\r\n",_fx_,
|
|
hStatus, pChannelRTPAddr));
|
|
// LOOKLOOK need to interpret hStatus
|
|
// let the channel know what happened.
|
|
pChannel->OnChannelOpen(CHANNEL_REJECTED);
|
|
|
|
// the channel knows what happened, so let it do the worrying.
|
|
return;
|
|
}
|
|
// what's the need for the different address types ????
|
|
if((pChannelRTPAddr->nAddrType != CC_IP_BINARY)
|
|
|| (pChannelRTCPAddr->nAddrType != CC_IP_BINARY))
|
|
{
|
|
ERRORMESSAGE(("%s: invalid address types %d, %d\r\n",_fx_,
|
|
pChannelRTPAddr->nAddrType, pChannelRTCPAddr->nAddrType));
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
// we now have the remote port info ( in host byte order)
|
|
sinD.sin_family = AF_INET;
|
|
sinD.sin_addr.S_un.S_addr = htonl(pChannelRTPAddr->Addr.IP_Binary.dwAddr);
|
|
sinD.sin_port = htons(pChannelRTPAddr->Addr.IP_Binary.wPort);
|
|
|
|
sinC.sin_family = AF_INET;
|
|
sinC.sin_addr.S_un.S_addr = htonl(pChannelRTCPAddr->Addr.IP_Binary.dwAddr);
|
|
// There are two ports, but in RTP, it is implicit
|
|
// that the RTCP control port is the next highest port number
|
|
// sinC.sin_port = htons(ntohs(pChannelAddr->Addr.IP_Binary.wPort) +1);
|
|
sinC.sin_port = htons(pChannelRTCPAddr->Addr.IP_Binary.wPort);
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s, opened on port 0x%04x, address 0x%08lX\r\n",_fx_,
|
|
pChannelRTPAddr->Addr.IP_Binary.wPort,pChannelRTPAddr->Addr.IP_Binary.dwAddr));
|
|
|
|
hrLast = pChannel->AcceptRemoteAddress(&sinD);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("OnChannelOpen: AcceptRemoteAddress failed\r\n"));
|
|
goto ERROR_EXIT;
|
|
}
|
|
hrLast = pChannel->AcceptRemoteRTCPAddress(&sinC);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("OnChannelOpen: AcceptRemoteRTCPAddress failed\r\n"));
|
|
goto ERROR_EXIT;
|
|
}
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen opening");
|
|
hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN); // the send side is open
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("OnChannelOpen:channel's OnChannelOpen() returned 0x%08lX\r\n", hrLast));
|
|
CloseChannel(pChannel);
|
|
goto ERROR_EXIT;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen open done");
|
|
m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_TX, pChannel, this);
|
|
//
|
|
// Check for readiness to notify that all required channels are open
|
|
//
|
|
CheckChannelsReady( );
|
|
SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen done");
|
|
return;
|
|
|
|
ERROR_EXIT:
|
|
// need to cleanup, disconnect, etc.
|
|
m_hCallCompleteCode = CCCI_CHANNEL_OPEN_ERROR;
|
|
// let the parent Conference object know about the imminent disconnect
|
|
DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
|
|
hrLast = CCO_E_MANDATORY_CHAN_OPEN_FAILED;
|
|
|
|
InternalDisconnect();
|
|
return;
|
|
}
|
|
VOID CH323Ctrl::OnRxChannelClose(HRESULT hStatus,
|
|
PCC_RX_CHANNEL_CLOSE_CALLBACK_PARAMS pChannelParams )
|
|
{
|
|
FX_ENTRY("CH323Ctrl::OnRxChannelClose");
|
|
PCC_ADDR pChannelRTPAddr;
|
|
PCC_ADDR pChannelRTCPAddr;
|
|
SOCKADDR_IN sinC, sinD;
|
|
|
|
ICtrlCommChan *pChannel;
|
|
if(hStatus != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s: hStatus:0x%08lX\r\n",_fx_,hStatus));
|
|
// LOOKLOOK need to interpret hStatus
|
|
}
|
|
if(!(pChannel = FindChannel(pChannelParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
return;
|
|
}
|
|
|
|
// validate channel - is this really a receive channel?
|
|
if(pChannel->IsSendChannel() == TRUE)
|
|
{
|
|
ERRORMESSAGE(("%s:not a receive channel:hChannel 0x%08lX\r\n",_fx_,
|
|
pChannelParams->hChannel));
|
|
return;
|
|
}
|
|
pChannel->OnChannelClose(CHANNEL_CLOSED);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID CH323Ctrl::OnTxChannelClose(HRESULT hStatus,
|
|
PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS pChannelParams )
|
|
{
|
|
FX_ENTRY("CH323Ctrl::OnTxChannelClose");
|
|
PCC_ADDR pChannelRTPAddr;
|
|
PCC_ADDR pChannelRTCPAddr;
|
|
SOCKADDR_IN sinC, sinD;
|
|
|
|
ICtrlCommChan *pChannel;
|
|
if(hStatus != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s: hStatus:0x%08lX\r\n",_fx_,hStatus));
|
|
// LOOKLOOK need to interpret hStatus
|
|
}
|
|
|
|
if(!(pChannel = FindChannel(pChannelParams->hChannel)))
|
|
{
|
|
CC_CloseChannelResponse(pChannelParams->hChannel, FALSE);
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
return;
|
|
}
|
|
|
|
// validate channel - is this really a send channel?
|
|
if(pChannel->IsSendChannel() == FALSE)
|
|
{
|
|
ERRORMESSAGE(("%s:not a send channel:hChannel 0x%08lX\r\n",_fx_,
|
|
pChannelParams->hChannel));
|
|
CC_CloseChannelResponse(pChannelParams->hChannel, FALSE);
|
|
return;
|
|
}
|
|
CC_CloseChannelResponse(pChannelParams->hChannel, TRUE);
|
|
pChannel->OnChannelClose(CHANNEL_CLOSED);
|
|
return;
|
|
}
|
|
|
|
BOOL CH323Ctrl::OnCallAccept(PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnCallAccept");
|
|
BOOL bRet = FALSE;
|
|
CH323Ctrl *pNewConnection = NULL;
|
|
if(m_Phase != CCS_Listening)
|
|
{
|
|
ERRORMESSAGE(("OnCallAccept: unexpected call, m_Phase = 0x%08lX\r\n", m_Phase));
|
|
goto EXIT;
|
|
}
|
|
|
|
if((!pListenCallbackParams->pCalleeAddr)
|
|
|| (pListenCallbackParams->pCalleeAddr->nAddrType != CC_IP_BINARY))
|
|
{
|
|
if(pListenCallbackParams->pCalleeAddr)
|
|
{
|
|
ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,pListenCallbackParams->pCalleeAddr->nAddrType));
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s: null local address\r\n",_fx_));
|
|
}
|
|
|
|
|
|
ERRORMESSAGE(("OnCallAccept:where's the local address????\r\n"));
|
|
PHOSTENT phe;
|
|
PSOCKADDR_IN psin;
|
|
char szTemp[200];
|
|
LPCSTR lpHostName;
|
|
gethostname(szTemp,sizeof(szTemp));
|
|
lpHostName = szTemp;
|
|
psin = &local_sin;
|
|
phe = gethostbyname(lpHostName);
|
|
if (phe != NULL)
|
|
{
|
|
memcpy((char FAR *)&(psin->sin_addr), phe->h_addr,phe->h_length);
|
|
psin->sin_family = AF_INET;
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// remember our local address
|
|
local_sin.sin_family = AF_INET;
|
|
// in host byte order
|
|
local_sin.sin_addr.S_un.S_addr = htonl(pListenCallbackParams->pCalleeAddr->Addr.IP_Binary.dwAddr);
|
|
// in host byte order
|
|
local_sin.sin_port = htons(pListenCallbackParams->pCalleeAddr->Addr.IP_Binary.wPort);
|
|
}
|
|
|
|
|
|
hrLast = m_pConfAdvise->GetAcceptingObject((LPIControlChannel *)&pNewConnection,
|
|
&m_PID);
|
|
if(HR_SUCCEEDED(hrLast) && pNewConnection)
|
|
{
|
|
// NOTE: The UI does not yet know this new object exists, and we may
|
|
// need to silently delete it if there is a disconnect or error
|
|
// Its ref count is 1 at this point. The decision to delete could be
|
|
// made inside pNewConnection->AcceptConnection(), (because sometimes
|
|
// socket reads complete synchronously depending on timing) SO, we need to
|
|
// protect the "unwind path" via AddRef() and Release() around the call
|
|
//
|
|
pNewConnection->AddRef(); //
|
|
hrLast = pNewConnection->AcceptConnection(this, pListenCallbackParams);
|
|
pNewConnection->Release();
|
|
if(HR_SUCCEEDED(hrLast))
|
|
{
|
|
// The Intel Call control DLL already did a socket accept, the
|
|
// Accept() methods simply initialize the handles and states of
|
|
// pNewConnection and get user information (caller ID)
|
|
// BUGBUG - the caller ID may change in Intel's code - it might
|
|
// come via a conference event
|
|
DEBUGMSG(ZONE_CONN,("OnCallAccept:accepted on connection 0x%08lX\r\n",pNewConnection));
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("OnCallAccept:Accept failed\r\n"));
|
|
// LOOK - Q: where does the accepting object get cleaned up?
|
|
// A: pNewConnection->AcceptConnection((LPIControlChannel)this)
|
|
// must call pNewConnection->DoAdvise(CCEV_ACCEPT_INCOMPLETE, NULL)
|
|
// if the error occurred before the conference object got involved,
|
|
// and must call InternalDisconnect() if the error occurred after
|
|
// the conference object got involved,
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("OnCallAccept:GetAcceptingObject failed, hr=0x%08lx\r\n",hrLast));
|
|
}
|
|
|
|
EXIT:
|
|
return bRet;
|
|
}
|
|
|
|
|
|
HRESULT CH323Ctrl::NewConference()
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::NewConference");
|
|
CapsCtl *pCapObject = NULL;
|
|
PCC_TERMCAPLIST pTermCaps = NULL;
|
|
CC_OCTETSTRING TerminalID;
|
|
PCC_TERMCAPDESCRIPTORS pCapsList = NULL;
|
|
LPWSTR lpwUserDisplayName;
|
|
|
|
hrLast = m_pConfAdvise->GetCapResolver((LPVOID *)&pCapObject, OID_CAP_ACM_TO_H323);
|
|
if(!HR_SUCCEEDED(hrLast) || (pCapObject == NULL))
|
|
{
|
|
ERRORMESSAGE(("%s: null resolver\r\n",_fx_));
|
|
goto EXIT;
|
|
}
|
|
if(m_hConference)
|
|
{
|
|
ERRORMESSAGE(("%s:leak or uninitialized m_hConference:0x%08lx\r\n",_fx_,
|
|
m_hConference));
|
|
}
|
|
|
|
hrLast = pCapObject->CreateCapList(&pTermCaps, &pCapsList);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
goto EXIT;
|
|
}
|
|
|
|
lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
|
|
if(lpwUserDisplayName)
|
|
{
|
|
TerminalID.pOctetString = (BYTE *)lpwUserDisplayName;
|
|
TerminalID.wOctetStringLength = (WORD)lstrlenW(lpwUserDisplayName)*sizeof(WCHAR);
|
|
}
|
|
|
|
// create a conference
|
|
hrLast = CC_CreateConference(&m_hConference, NULL,
|
|
0, // DWORD dwConferenceConfiguration,
|
|
pTermCaps, // PCC_TERMCAPLIST
|
|
pCapsList, // ptr to term cap descriptors (PCC_TERMCAPDESCRIPTORS)
|
|
&m_VendorInfo, // PVENDORINFO
|
|
(lpwUserDisplayName)? &TerminalID: NULL, // PCC_OCTETSTRING pTerminalID,
|
|
(DWORD_PTR)this,
|
|
NULL, // CC_TERMCAP_CONSTRUCTOR TermCapConstructor,
|
|
NULL, // CC_SESSIONTABLE_CONSTRUCTOR SessionTableConstructor,
|
|
CCConferenceCallback);
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s: CreateConference returned 0x%08lX\r\n", _fx_, hrLast));
|
|
|
|
}
|
|
|
|
EXIT:
|
|
pCapObject->DeleteCapList(pTermCaps, pCapsList);
|
|
return hrLast;
|
|
|
|
}
|
|
|
|
HRESULT CH323Ctrl::AcceptConnection(LPIControlChannel pIListenCtrlChan,
|
|
LPVOID lpvListenCallbackParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::AcceptConnection");
|
|
BOOL bRet = FALSE;
|
|
CREQ_RESPONSETYPE Response;
|
|
DWORD dwCode = CCR_LOCAL_SYSTEM_ERROR; // error variable only used in error case
|
|
ULONG ulNameSize, ulSize;
|
|
PSOCKADDR_IN psin;
|
|
LPWSTR lpwUserDisplayName;
|
|
CH323Ctrl *pListenConnection = (CH323Ctrl *)pIListenCtrlChan;
|
|
P_APP_CALL_SETUP_DATA pAppData = NULL;
|
|
APP_CALL_SETUP_DATA AppData;
|
|
|
|
PCC_NONSTANDARDDATA pNSData = ((PCC_LISTEN_CALLBACK_PARAMS)
|
|
lpvListenCallbackParams)->pNonStandardData;
|
|
|
|
if(pNSData
|
|
&& pNSData->bCountryCode == USA_H221_COUNTRY_CODE
|
|
// why be this picky -> && pNSData->bExtension == USA_H221_COUNTRY_EXTENSION;
|
|
&& pNSData->wManufacturerCode == MICROSOFT_H_221_MFG_CODE
|
|
&& pNSData->sData.pOctetString
|
|
&& pNSData->sData.wOctetStringLength >= sizeof(MSFT_NONSTANDARD_DATA))
|
|
{
|
|
if(((PMSFT_NONSTANDARD_DATA)pNSData->sData.pOctetString)->
|
|
data_type == NSTD_APPLICATION_DATA)
|
|
{
|
|
AppData.lpData = &((PMSFT_NONSTANDARD_DATA)pNSData->sData.pOctetString)->
|
|
nonstd_data.AppData.data;
|
|
AppData.dwDataSize = (DWORD)
|
|
((PMSFT_NONSTANDARD_DATA)pNSData->sData.pOctetString)->dw_nonstd_data_size;
|
|
pAppData = &AppData;
|
|
}
|
|
}
|
|
|
|
SetRemoteVendorID(((PCC_LISTEN_CALLBACK_PARAMS)lpvListenCallbackParams)->pVendorInfo);
|
|
|
|
// this object is assuming everything from the listening object, including
|
|
// the "Listening" state
|
|
|
|
// enter critical section and make sure another thread is not handling a caller disconnect
|
|
// or timeout
|
|
// EnterCriticalSection()
|
|
if(m_Phase == CCS_Idle)
|
|
{
|
|
GoNextPhase(CCS_Listening);
|
|
// once in this state, a disconnect on another thread will change the state
|
|
// to something besides CCS_Listening
|
|
|
|
pListenConnection->GetLocalAddress(&psin);
|
|
SetLocalAddress(psin);
|
|
|
|
// steal the conference ID from the listen object
|
|
// m_ConferenceID = pListenConnection->GetConfID();
|
|
memcpy(&m_ConferenceID, pListenConnection->GetConfIDptr(),sizeof(m_ConferenceID));
|
|
ZeroMemory(pListenConnection->GetConfIDptr(),sizeof(m_ConferenceID));
|
|
|
|
m_hCall = pListenConnection->GetHCall();
|
|
|
|
// steal the user info from the listen object
|
|
m_pRemoteAliasItem = pListenConnection->m_pRemoteAliasItem;
|
|
pListenConnection->m_pRemoteAliasItem = NULL; // make the listen object forget this
|
|
|
|
// steal the peer display name
|
|
pwszPeerDisplayName = pListenConnection->pwszPeerDisplayName;
|
|
pListenConnection->pwszPeerDisplayName = NULL;
|
|
|
|
lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
|
|
}
|
|
|
|
// else already timing out
|
|
// LeaveCriticalSection()
|
|
|
|
if (m_Phase != CCS_Listening) // cleanup before it gets accepted
|
|
{
|
|
goto ACCEPT_ERROR;
|
|
}
|
|
|
|
// let the conference object know that caller ID info is available
|
|
ConnectNotify(CCEV_CALLER_ID);
|
|
|
|
// Now going to do stuff that might put cleanup responsibility on the
|
|
// conference object or UI. (i.e. the call could be accepted)
|
|
|
|
// EnterCriticalSection()
|
|
if(m_Phase == CCS_Listening)
|
|
{
|
|
// state is still OK
|
|
GoNextPhase(CCS_Filtering);
|
|
// once in this state, a disconnect on another thread will change the state
|
|
// to something besides CCS_Filtering
|
|
}
|
|
// else already timing out
|
|
// LeaveCriticalSection()
|
|
|
|
if (m_Phase != CCS_Filtering) // one last chance to cleanup before it gets accepted
|
|
{
|
|
goto ERROR_REJECT;
|
|
}
|
|
|
|
// can't (should not) do this inside a critical section
|
|
// create a conference to accept the call
|
|
hrLast = NewConference();
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s, NewConference returned 0x%08lX\r\n", _fx_, hrLast));
|
|
goto ERROR_REJECT;
|
|
}
|
|
|
|
m_pConfAdvise->AddRef();
|
|
Response = m_pConfAdvise->FilterConnectionRequest(this, pAppData);
|
|
m_pConfAdvise->Release();
|
|
|
|
// Now it may be in the hands of the Conference object, and the accepted connection will
|
|
// need to go through the "disconnecting" state if cleanup is needed.
|
|
// Because connection code is reentrant, the connection could also have
|
|
// been torn down (via connection methods) while inside
|
|
// m_pConfAdvise->FilterConnectionRequest();
|
|
// In each case below, check validity of the connection state - it might have changed
|
|
// because a connection method was called or because the caller timed out
|
|
|
|
switch(Response)
|
|
{
|
|
default:
|
|
case CRR_ACCEPT:
|
|
if(m_Phase != CCS_Filtering)
|
|
{
|
|
ERRORMESSAGE(("%s, accepting state no longer valid 0x%08lX\r\n", _fx_, hrLast));
|
|
goto CANCEL_ACCEPT;
|
|
}
|
|
|
|
// accept this request
|
|
hrLast = CC_AcceptCall(m_hConference,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
lpwUserDisplayName,
|
|
m_hCall,
|
|
0, // DWORD dwBandwidth,
|
|
(DWORD_PTR)this);
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
m_ChanFlags &= ~CTRLF_OPEN;
|
|
goto CANCEL_ACCEPT;
|
|
}
|
|
|
|
GoNextPhase(CCS_Accepting);
|
|
bRet = TRUE;
|
|
|
|
break;
|
|
case CRR_ASYNC:
|
|
if(m_Phase == CCS_Accepting)
|
|
{
|
|
// then call has already been accepted inside FilterConnectionRequest callback
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if(m_Phase != CCS_Filtering)
|
|
{
|
|
ERRORMESSAGE(("%s, accepting state no longer valid 0x%08lX\r\n", _fx_, hrLast));
|
|
goto CANCEL_ACCEPT;
|
|
}
|
|
GoNextPhase(CCS_Ringing);
|
|
bRet = TRUE;
|
|
}
|
|
|
|
break;
|
|
case CRR_BUSY:
|
|
hrLast = CC_RejectCall(CC_REJECT_USER_BUSY,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
m_hCall);
|
|
// always clean up this object that's not accepting the call
|
|
GoNextPhase(CCS_Idle);
|
|
goto ACCEPT_ERROR;
|
|
break;
|
|
case CRR_REJECT:
|
|
hrLast = CC_RejectCall(CC_REJECT_DESTINATION_REJECTION,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
m_hCall);
|
|
// always clean up this object that's not accepting the call
|
|
GoNextPhase(CCS_Idle);
|
|
goto ACCEPT_ERROR;
|
|
break;
|
|
case CRR_SECURITY_DENIED:
|
|
hrLast = CC_RejectCall(CC_REJECT_SECURITY_DENIED,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
m_hCall);
|
|
// always clean up this object that's not accepting the call
|
|
GoNextPhase(CCS_Idle);
|
|
goto ACCEPT_ERROR;
|
|
break;
|
|
}
|
|
|
|
return hrLast;
|
|
ERROR_REJECT:
|
|
hrLast = CC_RejectCall(CC_REJECT_UNDEFINED_REASON,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
m_hCall); // always clean up this object that's not accepting the call
|
|
GoNextPhase(CCS_Idle);
|
|
|
|
ACCEPT_ERROR:
|
|
|
|
DoAdvise(CCEV_ACCEPT_INCOMPLETE, &dwCode);
|
|
return hrLast;
|
|
|
|
CANCEL_ACCEPT:
|
|
// InternalDisconnect() can be called from any state, and will do fine if
|
|
// it is already in a disconnecting state.
|
|
InternalDisconnect();
|
|
return hrLast;
|
|
}
|
|
|
|
|
|
VOID CH323Ctrl::Cleanup()
|
|
{
|
|
POSITION pos = m_ChannelList.GetHeadPosition();
|
|
ICtrlCommChan *pChan = NULL;
|
|
|
|
CleanupConferenceAttributes();
|
|
if(m_hConference)
|
|
{
|
|
hrLast = CC_DestroyConference(m_hConference, FALSE);
|
|
// LOOKLOOK - need to check return code!!!
|
|
m_hConference = 0;
|
|
}
|
|
|
|
// reset each channel (cleanup underlying socket references)
|
|
while (pos)
|
|
{
|
|
pChan = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
|
|
ASSERT(pChan);
|
|
// cleanup RTP sockets
|
|
pChan->Reset();
|
|
}
|
|
// clear "socket(s) are open flags
|
|
m_ChanFlags &= ~CTRLF_OPEN;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::GetLocalPort(PORT * lpPort)
|
|
{
|
|
*lpPort = ntohs(local_sin.sin_port);
|
|
return hrSuccess;
|
|
}
|
|
HRESULT CH323Ctrl::GetRemotePort(PORT * lpPort)
|
|
{
|
|
*lpPort = ntohs(remote_sin.sin_port);
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::GetLocalAddress(PSOCKADDR_IN *lplpAddr)
|
|
{
|
|
*lplpAddr = &local_sin;
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::GetRemoteAddress(PSOCKADDR_IN *lplpAddr)
|
|
{
|
|
*lplpAddr = &remote_sin;
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::ListenOn(PORT Port)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::ListenOn");
|
|
PCC_ALIASNAMES pAliasNames = m_pConfAdvise->GetUserAliases();
|
|
// temporary hack to override UI's ignorance of multiple protocol types
|
|
if(Port != H323_PORT)
|
|
{
|
|
ERRORMESSAGE(("%s, overriding port %d(d) with H323 port %d\r\n",_fx_,
|
|
Port, H323_PORT));
|
|
Port = H323_PORT;
|
|
}
|
|
|
|
// do we need to remember this?
|
|
local_sin.sin_addr.S_un.S_addr = INADDR_ANY;
|
|
local_sin.sin_family = AF_INET;
|
|
local_sin.sin_port = htons((u_short)Port); // set port
|
|
|
|
CC_ADDR ListenAddr;
|
|
|
|
ListenAddr.nAddrType = CC_IP_BINARY;
|
|
ListenAddr.bMulticast = FALSE;
|
|
// in host byte order
|
|
ListenAddr.Addr.IP_Binary.wPort = (u_short)Port;
|
|
ListenAddr.Addr.IP_Binary.dwAddr = ntohl(INADDR_ANY);
|
|
|
|
hrLast = CC_CallListen(&m_hListen, &ListenAddr,
|
|
pAliasNames, (DWORD_PTR)this, CCListenCallback);
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("CH323Ctrl::ListenOn:CallListen returned 0x%08lX\r\n", hrLast));
|
|
goto EXIT;
|
|
}
|
|
|
|
|
|
GoNextPhase(CCS_Listening);
|
|
m_ChanFlags = CTRLF_RESET;
|
|
hrLast = hrSuccess;
|
|
EXIT:
|
|
return hrLast;
|
|
}
|
|
HRESULT CH323Ctrl::StopListen(VOID)
|
|
{
|
|
if(m_Phase == CCS_Listening)
|
|
{
|
|
hrLast = CC_CancelListen(m_hListen);
|
|
}
|
|
else
|
|
{
|
|
hrLast = CCO_E_NOT_LISTENING;
|
|
}
|
|
|
|
//EXIT:
|
|
return hrLast;
|
|
}
|
|
|
|
|
|
HRESULT CH323Ctrl::AsyncAcceptRejectCall(CREQ_RESPONSETYPE Response)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::AsyncAcceptRejectCall");
|
|
HRESULT hr = CCO_E_CONNECT_FAILED;
|
|
LPWSTR lpwUserDisplayName;
|
|
|
|
if(Response == CRR_ACCEPT)
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s:accepting\r\n",_fx_));
|
|
lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
|
|
// check call setup phase - send ready if user's acceptance is what
|
|
// was holding us up
|
|
if((m_Phase == CCS_Ringing) || (m_Phase == CCS_Filtering))
|
|
{
|
|
// accept this request
|
|
hrLast = CC_AcceptCall(m_hConference,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
lpwUserDisplayName,
|
|
m_hCall,
|
|
0, // DWORD dwBandwidth,
|
|
(DWORD_PTR)this);
|
|
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, CC_AcceptCall() returned 0x%08lX\r\n",_fx_, hrLast));
|
|
goto EXIT;
|
|
}
|
|
GoNextPhase(CCS_Accepting);
|
|
hr = hrSuccess;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// reject only if in accepting state(s)
|
|
// deletion is possible while in advise callback, so protect w/ AddRef()
|
|
AddRef();
|
|
DEBUGMSG(ZONE_CONN,("%s:rejecting\r\n",_fx_));
|
|
|
|
if((m_Phase == CCS_Ringing) || (m_Phase == CCS_Filtering))
|
|
{
|
|
hrLast = CC_RejectCall((Response == CRR_BUSY) ?
|
|
CC_REJECT_USER_BUSY : CC_REJECT_DESTINATION_REJECTION,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
m_hCall);
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s, CC_RejectCall() returned 0x%08lX\r\n",_fx_, hrLast));
|
|
}
|
|
GoNextPhase(CCS_Idle);
|
|
// notify the UI or application code or whatever..
|
|
DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
|
|
}
|
|
else
|
|
{
|
|
hr = CCO_E_INVALID_PARAM; // LOOKLOOK - need INVALID_STATE error code
|
|
}
|
|
|
|
Release();
|
|
}
|
|
EXIT:
|
|
return (hr);
|
|
}
|
|
|
|
|
|
ULONG CH323Ctrl ::AddRef()
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::AddRef");
|
|
uRef++;
|
|
DEBUGMSG(ZONE_REFCOUNT,("%s:(0x%08lX)->AddRef() uRef = 0x%08lX\r\n",_fx_, this, uRef ));
|
|
return uRef;
|
|
}
|
|
|
|
ULONG CH323Ctrl ::Release()
|
|
{
|
|
FX_ENTRY("CH323Ctrl ::Release");
|
|
uRef--;
|
|
if(uRef == 0)
|
|
{
|
|
DEBUGMSG(ZONE_CONN,("%s:(0x%08lX)->Releasing in phase:%d\r\n",_fx_, this, m_Phase ));
|
|
|
|
if(m_Phase != CCS_Idle)
|
|
{
|
|
ERRORMESSAGE(("CMSIACtrl::uRef zero in non idle (%d) state!\r\n",m_Phase));
|
|
InternalDisconnect();
|
|
}
|
|
delete this;
|
|
return 0;
|
|
}
|
|
DEBUGMSG(ZONE_REFCOUNT,("%s:(0x%08lX)->Release() uRef = 0x%08lX\r\n",_fx_, this, uRef ));
|
|
return uRef;
|
|
}
|
|
|
|
|
|
|
|
// implement IControlChannel::Disconnect(). Map reason codes to the protocol.
|
|
VOID CH323Ctrl::Disconnect(DWORD dwReason)
|
|
{
|
|
// no way to propagate reason through H.323 stack?????
|
|
InternalDisconnect();
|
|
}
|
|
|
|
VOID CH323Ctrl::InternalDisconnect()
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::Disconnect");
|
|
SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect");
|
|
|
|
m_ChanFlags &= ~CTRLF_ORIGINATING; // reset "originating" flag.
|
|
|
|
DEBUGMSG(ZONE_CONN,("%s, called in state %d, uRef = 0x%08lX\r\n",_fx_, m_Phase, uRef));
|
|
switch(m_Phase)
|
|
{
|
|
case CCS_Connecting:
|
|
case CCS_Accepting:
|
|
// if we believe the control channel is still connected, disconnect
|
|
if(IsCtlChanOpen(m_ChanFlags))
|
|
{
|
|
//set state to indicate disconnecting.
|
|
GoNextPhase(CCS_Disconnecting);
|
|
DEBUGMSG(ZONE_CONN,("%s, Expecting a CC_HANGUP_INDICATION\r\n",_fx_));
|
|
hrLast = CC_Hangup(m_hConference, FALSE, (DWORD_PTR)this);
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s:Hangup() returned 0x%08lX\r\n",_fx_, hrLast));
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect hangup done");
|
|
}
|
|
else
|
|
{
|
|
CC_CancelCall(m_hCall);
|
|
GoNextPhase(CCS_Idle); // no need to ck retval - we're disconnected
|
|
// notify the UI or application code or whatever..
|
|
DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
|
|
}
|
|
break;
|
|
case CCS_Ringing:
|
|
// The call has not yet been accepted!!! Reject it!
|
|
hrLast = CC_RejectCall(CC_REJECT_UNDEFINED_REASON,
|
|
NULL, // PCC_NONSTANDARDDATA pNonStandardData
|
|
m_hCall);
|
|
SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect reject done");
|
|
|
|
GoNextPhase(CCS_Idle);
|
|
// notify the UI or application code or whatever..
|
|
DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
|
|
break;
|
|
case CCS_Idle:
|
|
case CCS_Disconnecting:
|
|
ERRORMESSAGE(("%s:called in unconnected state %d\r\n",_fx_, m_Phase));
|
|
break;
|
|
default:
|
|
//CCS_Ringing
|
|
//CCS_Opening
|
|
//CCS_Closing
|
|
//CCS_Ready
|
|
//CCS_InUse
|
|
//CCS_Listening
|
|
|
|
// if we believe the control channel is still connected, disconnect
|
|
if(IsCtlChanOpen(m_ChanFlags))
|
|
{
|
|
//set state to indicate disconnecting.
|
|
GoNextPhase(CCS_Disconnecting);
|
|
hrLast = CC_Hangup(m_hConference, FALSE, (DWORD_PTR)this);
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("%s:Hangup() returned 0x%08lX\r\n",_fx_, hrLast));
|
|
DoAdvise(CCEV_DISCONNECTED ,NULL);
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect hangup done");
|
|
}
|
|
else
|
|
{
|
|
GoNextPhase(CCS_Idle); // no need to ck retval - we're disconnected
|
|
// notify the UI or application code or whatever..
|
|
DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
|
|
}
|
|
break;
|
|
}
|
|
SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect done");
|
|
}
|
|
|
|
|
|
|
|
// start the asynchronous stuff that will instantiate a control channel
|
|
HRESULT CH323Ctrl::PlaceCall (BOOL bUseGKResolution, PSOCKADDR_IN pCallAddr,
|
|
P_H323ALIASLIST pDestinationAliases, P_H323ALIASLIST pExtraAliases,
|
|
LPCWSTR pCalledPartyNumber, P_APP_CALL_SETUP_DATA pAppData)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::PlaceCall");
|
|
CC_ALIASNAMES pstn_alias;
|
|
PCC_ALIASITEM pPSTNAlias = NULL;
|
|
PCC_ALIASNAMES pRemoteAliasNames = NULL;
|
|
PCC_ALIASNAMES pTranslatedAliasNames = NULL;
|
|
PCC_ALIASNAMES pLocalAliasNames = NULL;
|
|
PCC_ADDR pDestinationAddr = NULL;
|
|
PCC_ADDR pConnectAddr = NULL;
|
|
LPWSTR lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
|
|
PCC_NONSTANDARDDATA pNSData = NULL;
|
|
PMSFT_NONSTANDARD_DATA lpNonstdContent = NULL;
|
|
int iLen;
|
|
LPWSTR lpwszDest;
|
|
HRESULT hResult = hrSuccess;
|
|
// validate current state, don't allow bad actions
|
|
if(m_Phase != CCS_Idle)
|
|
{
|
|
hResult = CCO_E_NOT_IDLE;
|
|
goto EXIT;
|
|
}
|
|
|
|
OBJ_CPT_RESET; // reset elapsed timer
|
|
|
|
m_ChanFlags |= CTRLF_INIT_ORIGINATING;
|
|
if(!pCallAddr)
|
|
{
|
|
hResult = CCO_E_BAD_ADDRESS;
|
|
goto EXIT;
|
|
}
|
|
else
|
|
{
|
|
// keep a copy of the address
|
|
SetRemoteAddress(pCallAddr);
|
|
}
|
|
// temporary hack to override UI's ignorance of multiple protocol types
|
|
if(remote_sin.sin_port != htons(H323_PORT))
|
|
{
|
|
ERRORMESSAGE(("%s, overriding port %d(d) with H323 port %d\r\n",_fx_,
|
|
ntohs(remote_sin.sin_port), H323_PORT));
|
|
remote_sin.sin_port = htons(H323_PORT);
|
|
}
|
|
|
|
// check for connecting to self (not supported)
|
|
if(local_sin.sin_addr.s_addr == remote_sin.sin_addr.s_addr)
|
|
{
|
|
hResult = CCO_E_BAD_ADDRESS;
|
|
goto EXIT;
|
|
}
|
|
|
|
if(m_pRemoteAliasItem)
|
|
{
|
|
MemFree(m_pRemoteAliasItem);
|
|
m_pRemoteAliasItem = NULL;
|
|
}
|
|
|
|
// Is this a PSTN or H.320 gateway call?
|
|
if(pCalledPartyNumber)
|
|
{
|
|
// Then, due to the bogus way that CC_PlaceCall() is overloaded, the remote alias names
|
|
// must be overridden with the E.164 phone number. The hack is buried in
|
|
// Q931ConnectCallback() in CALLCONT.DLL (thank you Intel). That hack propagates
|
|
// the phone number to the "CalledPartyNumber" of the SETUP message only if there is
|
|
// exactly one alias, and that one alias is of type E.164.
|
|
|
|
// get # of characters
|
|
iLen = lstrlenW(pCalledPartyNumber);
|
|
// need buffer of size CC_ALIASITEM plus the size (in bytes) of the string
|
|
pPSTNAlias = (PCC_ALIASITEM)MemAlloc(sizeof(CC_ALIASITEM)
|
|
+ sizeof(WCHAR)* (iLen+1));
|
|
if(!pPSTNAlias)
|
|
{
|
|
ERRORMESSAGE(("%s:failed alloc of pPSTNAlias:0x%08lx\r\n",_fx_));
|
|
hResult = CCO_E_OUT_OF_MEMORY;
|
|
goto EXIT;
|
|
}
|
|
|
|
WORD wIndex, wLength =1; // init wLength to count the null terminator
|
|
WCHAR E164Chars[] = {CC_ALIAS_H323_PHONE_CHARS};
|
|
LPCWSTR lpSrc = pCalledPartyNumber;
|
|
pPSTNAlias->wType = CC_ALIAS_H323_PHONE;
|
|
// set offsets - the E.164 address (a phone number) is the only thing
|
|
// in the alias name buffer
|
|
lpwszDest = (LPWSTR)(((char *)pPSTNAlias)+ sizeof(CC_ALIASITEM));
|
|
pPSTNAlias->pData = lpwszDest;
|
|
while(iLen--)
|
|
{
|
|
wIndex = (sizeof(E164Chars)/sizeof (WCHAR)) -1; //scan E164Chars[]
|
|
do
|
|
{
|
|
if(*lpSrc == E164Chars[wIndex])
|
|
{
|
|
*lpwszDest++ = *lpSrc;
|
|
wLength++;
|
|
break;
|
|
}
|
|
}while(wIndex--);
|
|
|
|
lpSrc++;
|
|
}
|
|
// terminate it
|
|
*lpwszDest = 0;
|
|
|
|
// wDataLength is the # of UNICODE characters
|
|
pPSTNAlias->wDataLength = wLength;
|
|
pstn_alias.wCount = 1;
|
|
pstn_alias.pItems = pPSTNAlias;
|
|
pRemoteAliasNames = &pstn_alias;
|
|
|
|
}
|
|
else if (pDestinationAliases && bUseGKResolution)// use the supplied callee alias names
|
|
{
|
|
hrLast = AllocTranslatedAliasList(&pTranslatedAliasNames, pDestinationAliases);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s, AllocTranslatedAliasList returned 0x%08lX\r\n", _fx_, hrLast));
|
|
hResult = CCO_E_SYSTEM_ERROR;
|
|
goto EXIT;
|
|
}
|
|
pRemoteAliasNames = pTranslatedAliasNames;
|
|
}
|
|
// else pRemoteAliasNames is initialized to NULL
|
|
|
|
|
|
pLocalAliasNames = m_pConfAdvise->GetUserAliases();
|
|
// start!!!
|
|
CC_ADDR ConfAddr;
|
|
// fixup the intel version of the address
|
|
// also note that it's all in host byte order
|
|
ConfAddr.bMulticast = FALSE;
|
|
ConfAddr.nAddrType = CC_IP_BINARY;
|
|
//hrLast = GetRemotePort(&ConfAddr.Addr.IP_Binary.wPort);
|
|
ConfAddr.Addr.IP_Binary.wPort = htons(remote_sin.sin_port);
|
|
ConfAddr.Addr.IP_Binary.dwAddr = ntohl(remote_sin.sin_addr.S_un.S_addr);
|
|
|
|
#ifdef DEBUG
|
|
if(m_hConference)
|
|
ERRORMESSAGE(("%s:leak or uninitialized m_hConference:0x%08lx\r\n",_fx_,
|
|
m_hConference));
|
|
#endif // DEBUG
|
|
|
|
// create a conference to place the call
|
|
SHOW_OBJ_ETIME("PlaceCall ready to create conference");
|
|
hrLast = NewConference();
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
ERRORMESSAGE(("%s, NewConference returned 0x%08lX\r\n", _fx_, hrLast));
|
|
hResult = CCO_E_SYSTEM_ERROR;
|
|
goto EXIT;
|
|
}
|
|
|
|
|
|
// Set connect timeout value
|
|
// LOOKLOOK - this is a hardcoded value - !!! Where should this actualy come from?
|
|
// 30 secs == 30000mS
|
|
SHOW_OBJ_ETIME("PlaceCall setting timeout");
|
|
|
|
hrLast = CC_SetCallControlTimeout(CC_Q931_ALERTING_TIMEOUT, 30000);
|
|
|
|
if(pAppData)
|
|
{
|
|
// typical case - app data should be really small
|
|
if(pAppData->dwDataSize <= APPLICATION_DATA_DEFAULT_SIZE)
|
|
{
|
|
m_NonstdContent.data_type = NSTD_APPLICATION_DATA;
|
|
m_NonstdContent.dw_nonstd_data_size = pAppData->dwDataSize;
|
|
memcpy(&m_NonstdContent.nonstd_data.AppData.data,
|
|
pAppData->lpData, pAppData->dwDataSize);
|
|
m_NonstandardData.sData.pOctetString = (LPBYTE) &m_NonstdContent;
|
|
m_NonstandardData.sData.wOctetStringLength = sizeof(m_NonstdContent);
|
|
}
|
|
else // need some heap
|
|
{
|
|
UINT uTotalSize = sizeof(MSFT_NONSTANDARD_DATA)+ pAppData->dwDataSize;
|
|
lpNonstdContent = (PMSFT_NONSTANDARD_DATA)MemAlloc(uTotalSize);
|
|
if(lpNonstdContent)
|
|
{
|
|
lpNonstdContent->data_type = NSTD_APPLICATION_DATA;
|
|
lpNonstdContent->dw_nonstd_data_size = pAppData->dwDataSize;
|
|
memcpy(&lpNonstdContent->nonstd_data.AppData.data, pAppData->lpData,pAppData->dwDataSize);
|
|
m_NonstandardData.sData.pOctetString = (LPBYTE) lpNonstdContent;
|
|
m_NonstandardData.sData.wOctetStringLength = LOWORD(uTotalSize);
|
|
}
|
|
else
|
|
{
|
|
ERRORMESSAGE(("%s, alloc failed\r\n", _fx_));
|
|
hResult = CCO_E_SYSTEM_ERROR;
|
|
goto EXIT;
|
|
}
|
|
}
|
|
pNSData = &m_NonstandardData;
|
|
}
|
|
|
|
m_NonstandardData.bCountryCode = USA_H221_COUNTRY_CODE;
|
|
m_NonstandardData.bExtension = USA_H221_COUNTRY_EXTENSION;
|
|
m_NonstandardData.wManufacturerCode = MICROSOFT_H_221_MFG_CODE;
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::PlaceCall ready to place call");
|
|
|
|
// set destination address pointers
|
|
if(bUseGKResolution)
|
|
{
|
|
// the address passed in pCallAddr is the GK's address
|
|
pConnectAddr = &ConfAddr;
|
|
}
|
|
else
|
|
{
|
|
pDestinationAddr = &ConfAddr;
|
|
}
|
|
hrLast = CC_PlaceCall(
|
|
m_hConference,
|
|
&m_hCall,
|
|
pLocalAliasNames, // PCC_ALIASNAMES pLocalAliasNames,
|
|
pRemoteAliasNames,
|
|
NULL, // PCC_ALIASNAMES pExtraCalleeAliasNames,
|
|
NULL, // PCC_ALIASITEM pCalleeExtension,
|
|
pNSData, // PCC_NONSTANDARDDATA pNonStandardData,
|
|
lpwUserDisplayName, // PWSTR pszDisplay,
|
|
pDestinationAddr, // Destination call signalling address
|
|
pConnectAddr, // address to send the SETUP message to, if different than
|
|
// the destination address. (used for gatekeeper calls?)
|
|
0, // DWORD dwBandwidth,
|
|
(DWORD_PTR) this);
|
|
|
|
SHOW_OBJ_ETIME("CH323Ctrl::PlaceCall placed call");
|
|
|
|
// clear these out so that cleanup does not try to free later
|
|
if(lpNonstdContent)
|
|
MemFree(lpNonstdContent);
|
|
m_NonstandardData.sData.pOctetString = NULL;
|
|
m_NonstandardData.sData.wOctetStringLength = 0;
|
|
|
|
// check return from CC_PlaceCall
|
|
if(hrLast != CC_OK)
|
|
{
|
|
ERRORMESSAGE(("CH323Ctrl::PlaceCall, PlaceCall returned 0x%08lX\r\n", hrLast));
|
|
hResult = CCO_E_CONNECT_FAILED;
|
|
goto EXIT;
|
|
}
|
|
// wait for an indication
|
|
GoNextPhase(CCS_Connecting);
|
|
|
|
EXIT:
|
|
if(pTranslatedAliasNames)
|
|
{
|
|
FreeTranslatedAliasList(pTranslatedAliasNames);
|
|
}
|
|
if(pPSTNAlias)
|
|
{
|
|
MemFree(pPSTNAlias);
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
//
|
|
// Given HCHANNEL, find the channel object.
|
|
//
|
|
|
|
ICtrlCommChan *CH323Ctrl::FindChannel(CC_HCHANNEL hChannel)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::FindChannel");
|
|
// find the channel
|
|
|
|
POSITION pos = m_ChannelList.GetHeadPosition();
|
|
ICtrlCommChan *pChannel;
|
|
while (pos)
|
|
{
|
|
pChannel = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
|
|
ASSERT(pChannel);
|
|
if(pChannel->GetHChannel() == hChannel)
|
|
return pChannel;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// fallout to error case
|
|
ERRORMESSAGE(("%s, did not find hChannel 0x%08lX\r\n",_fx_,hChannel));
|
|
#endif // DEBUG
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID CH323Ctrl::OnMute(HRESULT hStatus,
|
|
PCC_MUTE_CALLBACK_PARAMS pParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnMute");
|
|
ICtrlCommChan *pChannel;
|
|
HRESULT hr;
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
return;
|
|
}
|
|
hr = pChannel->PauseNet(TRUE, TRUE);
|
|
if(!HR_SUCCEEDED(hr))
|
|
{
|
|
ERRORMESSAGE(("%s, Pausenet returned 0x%08lx\r\n", _fx_, hr));
|
|
}
|
|
}
|
|
VOID CH323Ctrl::OnUnMute(HRESULT hStatus,
|
|
PCC_UNMUTE_CALLBACK_PARAMS pParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnUnMute");
|
|
ICtrlCommChan *pChannel;
|
|
HRESULT hr;
|
|
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
return;
|
|
}
|
|
hr = pChannel->PauseNet(FALSE, TRUE);
|
|
if(!HR_SUCCEEDED(hr))
|
|
{
|
|
ERRORMESSAGE(("%s, Pausenet returned 0x%08lx\r\n", _fx_, hr));
|
|
}
|
|
}
|
|
|
|
|
|
VOID CH323Ctrl::OnMiscCommand(HRESULT hStatus,
|
|
PCC_H245_MISCELLANEOUS_COMMAND_CALLBACK_PARAMS pParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnMiscCommand");
|
|
ICtrlCommChan *pChannel;
|
|
|
|
// not every command references an individual channel. The 4 exceptions are:
|
|
// case equaliseDelay_chosen:
|
|
// case zeroDelay_chosen:
|
|
// case multipointModeCommand_chosen:
|
|
// case cnclMltpntMdCmmnd_chosen:
|
|
//
|
|
// if we were betting on receiving few of the exceptional cases, we would always
|
|
// try to find the channel.
|
|
//if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
//{
|
|
// ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
// but don't error because of the exceptions
|
|
//}
|
|
|
|
switch(pParams->pMiscellaneousCommand->type.choice)
|
|
{
|
|
// the name and spelling of these constants was invented by the OSS compiler
|
|
//
|
|
case videoFreezePicture_chosen:
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case videoFastUpdatePicture_chosen: // the receiver wants an I-Frame
|
|
{
|
|
HRESULT hr;
|
|
IVideoChannel *pIVC=NULL;
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
break;
|
|
}
|
|
hr = pChannel->QueryInterface(IID_IVideoChannel, (void **)&pIVC);
|
|
if(HR_SUCCEEDED(hr))
|
|
{
|
|
pIVC->SendKeyFrame();
|
|
pIVC->Release();
|
|
}
|
|
// else it must not be a video channel
|
|
|
|
}
|
|
break;
|
|
case MCd_tp_vdTmprlSptlTrdOff_chosen:
|
|
{
|
|
DWORD dwTradeoff;
|
|
HRESULT hr;
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
break;
|
|
}
|
|
// set TS value of the channel, also propagate to Datapump
|
|
dwTradeoff = MAKELONG(
|
|
pParams->pMiscellaneousCommand->type.u.MCd_tp_vdTmprlSptlTrdOff, 0);
|
|
// set channel property
|
|
// NOTE: when PROP_TS_TRADEOFF is set, the channel does all the
|
|
// local tweaking to make it happen. The channel will also signal the
|
|
// new value to the remote as if the local end initiated it.
|
|
hr = pChannel->CtrlChanSetProperty(PROP_TS_TRADEOFF, &dwTradeoff, sizeof(dwTradeoff));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// the following are not currently handled
|
|
// case equaliseDelay_chosen:
|
|
// case zeroDelay_chosen:
|
|
// case videoSendSyncEveryGOB_chosen:
|
|
// case vdSndSyncEvryGOBCncl_chosen:
|
|
// case videoFastUpdateGOB_chosen: // suposedly required by H.323
|
|
// case videoFastUpdateMB_chosen: // suposedly required by H.323
|
|
|
|
// and the remaining 2 are handled by the call control layer
|
|
// so we will never see these
|
|
// case multipointModeCommand_chosen:
|
|
// case cnclMltpntMdCmmnd_chosen:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
VOID CH323Ctrl::OnMiscIndication(HRESULT hStatus,
|
|
PCC_H245_MISCELLANEOUS_INDICATION_CALLBACK_PARAMS pParams)
|
|
{
|
|
FX_ENTRY ("CH323Ctrl::OnMiscIndication");
|
|
ICtrlCommChan *pChannel;
|
|
HRESULT hr;
|
|
unsigned short choice = pParams->pMiscellaneousIndication->type.choice;
|
|
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
// check the exceptional cases for which this is OK
|
|
if((choice == multipointConference_chosen)
|
|
|| (choice == cnclMltpntCnfrnc_chosen)
|
|
|| (choice == multipointZeroComm_chosen)
|
|
|| (choice == cancelMultipointZeroComm_chosen)
|
|
|| (choice == mltpntScndryStts_chosen)
|
|
|| (choice == cnclMltpntScndryStts_chosen))
|
|
{
|
|
return; // as long as the above choices are not supported......
|
|
}
|
|
|
|
}
|
|
switch(choice)
|
|
{
|
|
case logicalChannelActive_chosen:
|
|
if(NULL != pChannel)
|
|
{
|
|
hr = pChannel->PauseNet(FALSE, TRUE);
|
|
}
|
|
break;
|
|
case logicalChannelInactive_chosen:
|
|
if(NULL != pChannel)
|
|
{
|
|
hr = pChannel->PauseNet(TRUE, TRUE);
|
|
}
|
|
break;
|
|
|
|
case MIn_tp_vdTmprlSptlTrdOff_chosen:
|
|
{
|
|
DWORD dwTradeoff = MAKELONG(0,
|
|
pParams->pMiscellaneousIndication->type.u.MIn_tp_vdTmprlSptlTrdOff);
|
|
|
|
if(!(pChannel = FindChannel(pParams->hChannel)))
|
|
{
|
|
ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
|
|
break;
|
|
}
|
|
// Set the indicated TS value of the channel.
|
|
// This should never occur for send channels.
|
|
//
|
|
hr = pChannel->CtrlChanSetProperty(PROP_TS_TRADEOFF_IND, &dwTradeoff, sizeof(dwTradeoff));
|
|
}
|
|
break;
|
|
|
|
// the following are not currently handled
|
|
// case multipointConference_chosen:
|
|
// case cnclMltpntCnfrnc_chosen:
|
|
// case multipointZeroComm_chosen:
|
|
// case cancelMultipointZeroComm_chosen:
|
|
// case mltpntScndryStts_chosen:
|
|
// case cnclMltpntScndryStts_chosen:
|
|
// case vdIndctRdyTActvt_chosen:
|
|
// case videoNotDecodedMBs_chosen:
|
|
|
|
}
|
|
}
|
|
|
|
HRESULT CH323Ctrl::MiscChannelCommand(
|
|
ICtrlCommChan *pChannel,
|
|
VOID * pCmd)
|
|
{
|
|
|
|
#ifdef BETA_2_ASN_PRESENT
|
|
if(m_fAvoidCrashingPDUs)
|
|
return hrSuccess;
|
|
#endif // BETA_2_ASN_PRESENT
|
|
|
|
return CC_H245MiscellaneousCommand(m_hCall, pChannel->GetHChannel(),
|
|
(MiscellaneousCommand *)pCmd);
|
|
}
|
|
|
|
HRESULT CH323Ctrl::MiscChannelIndication(
|
|
ICtrlCommChan *pChannel,
|
|
VOID * pInd)
|
|
{
|
|
MiscellaneousIndication *pMI = (MiscellaneousIndication *)pInd;
|
|
|
|
#ifdef BETA_2_ASN_PRESENT
|
|
if(m_fAvoidCrashingPDUs)
|
|
return hrSuccess;
|
|
#endif
|
|
|
|
// Intel decided that they had to wrap two Misc commands with two separate,
|
|
// additional APIs. And it won't allow those to be issued any other way.
|
|
// (it returns an error). Until we fix that, need to catch and reroute those
|
|
// two special ones
|
|
if(pMI->type.choice == logicalChannelActive_chosen)
|
|
{
|
|
return CC_UnMute(pChannel->GetHChannel());
|
|
}
|
|
else if (pMI->type.choice == logicalChannelInactive_chosen )
|
|
{
|
|
return CC_Mute(pChannel->GetHChannel());
|
|
}
|
|
else
|
|
return CC_H245MiscellaneousIndication(m_hCall,pChannel->GetHChannel(),pMI);
|
|
|
|
}
|
|
|
|
VOID CH323Ctrl::SetRemoteVendorID(PCC_VENDORINFO pVendorInfo)
|
|
{
|
|
if(!pVendorInfo)
|
|
return;
|
|
|
|
m_RemoteVendorInfo.bCountryCode = pVendorInfo->bCountryCode;
|
|
m_RemoteVendorInfo.bExtension = pVendorInfo->bExtension;
|
|
m_RemoteVendorInfo.wManufacturerCode = pVendorInfo->wManufacturerCode;
|
|
if(pVendorInfo->pProductNumber
|
|
&& pVendorInfo->pProductNumber->wOctetStringLength
|
|
&& pVendorInfo->pProductNumber->pOctetString)
|
|
{
|
|
if(m_RemoteVendorInfo.pProductNumber)
|
|
{
|
|
MemFree(m_RemoteVendorInfo.pProductNumber);
|
|
}
|
|
m_RemoteVendorInfo.pProductNumber = (PCC_OCTETSTRING)
|
|
MemAlloc(sizeof(CC_OCTETSTRING)
|
|
+ pVendorInfo->pProductNumber->wOctetStringLength);
|
|
if(m_RemoteVendorInfo.pProductNumber)
|
|
{
|
|
m_RemoteVendorInfo.pProductNumber->wOctetStringLength
|
|
= pVendorInfo->pProductNumber->wOctetStringLength;
|
|
m_RemoteVendorInfo.pProductNumber->pOctetString =
|
|
((BYTE *)m_RemoteVendorInfo.pProductNumber + sizeof(CC_OCTETSTRING));
|
|
memcpy(m_RemoteVendorInfo.pProductNumber->pOctetString,
|
|
pVendorInfo->pProductNumber->pOctetString,
|
|
pVendorInfo->pProductNumber->wOctetStringLength);
|
|
}
|
|
|
|
}
|
|
if(pVendorInfo->pVersionNumber)
|
|
{
|
|
if(m_RemoteVendorInfo.pVersionNumber)
|
|
{
|
|
MemFree(m_RemoteVendorInfo.pVersionNumber);
|
|
}
|
|
m_RemoteVendorInfo.pVersionNumber = (PCC_OCTETSTRING)
|
|
MemAlloc(sizeof(CC_OCTETSTRING)
|
|
+ pVendorInfo->pVersionNumber->wOctetStringLength);
|
|
if(m_RemoteVendorInfo.pVersionNumber)
|
|
{
|
|
m_RemoteVendorInfo.pVersionNumber->wOctetStringLength
|
|
= pVendorInfo->pVersionNumber->wOctetStringLength;
|
|
m_RemoteVendorInfo.pVersionNumber->pOctetString =
|
|
((BYTE *)m_RemoteVendorInfo.pVersionNumber + sizeof(CC_OCTETSTRING));
|
|
memcpy(m_RemoteVendorInfo.pVersionNumber->pOctetString,
|
|
pVendorInfo->pVersionNumber->pOctetString,
|
|
pVendorInfo->pVersionNumber->wOctetStringLength);
|
|
}
|
|
}
|
|
#ifdef BETA_2_ASN_PRESENT
|
|
char IntelCrashingID[] = "Intel Internet Video Phone";
|
|
char IntelCrashingVer[] = "1.0";
|
|
|
|
m_fAvoidCrashingPDUs = FALSE; // innocent until proven guilty
|
|
if(m_RemoteVendorInfo.bCountryCode == USA_H221_COUNTRY_CODE)
|
|
{
|
|
// then it's possible that it is Intel or Microsoft
|
|
if(m_RemoteVendorInfo.wManufacturerCode == MICROSOFT_H_221_MFG_CODE)
|
|
{
|
|
if((!pVendorInfo->pProductNumber) && (!pVendorInfo->pVersionNumber))
|
|
{
|
|
// safe to assume this is Beta2 or Beta3
|
|
m_fAvoidCrashingPDUs = TRUE;
|
|
}
|
|
else if((pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->wOctetStringLength == 0)
|
|
&& (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->wOctetStringLength == 0))
|
|
{
|
|
// safe to assume this is Beta2 or Beta3
|
|
m_fAvoidCrashingPDUs = TRUE;
|
|
}
|
|
}
|
|
else if(m_RemoteVendorInfo.wManufacturerCode == INTEL_H_221_MFG_CODE)
|
|
{
|
|
if(pVendorInfo->pProductNumber
|
|
&& pVendorInfo->pVersionNumber
|
|
&& pVendorInfo->pProductNumber->wOctetStringLength
|
|
&& pVendorInfo->pProductNumber->pOctetString
|
|
&& pVendorInfo->pVersionNumber->wOctetStringLength
|
|
&& pVendorInfo->pVersionNumber->pOctetString)
|
|
|
|
{
|
|
// compare strings, don't care about null terminator
|
|
if((0 == memcmp(pVendorInfo->pProductNumber->pOctetString,
|
|
IntelCrashingID, min(sizeof(IntelCrashingID)-1,pVendorInfo->pProductNumber->wOctetStringLength)))
|
|
&& (0 == memcmp(pVendorInfo->pVersionNumber->pOctetString,
|
|
IntelCrashingVer,
|
|
min(sizeof(IntelCrashingVer)-1, pVendorInfo->pVersionNumber->wOctetStringLength)) ))
|
|
{
|
|
m_fAvoidCrashingPDUs = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif //BETA_2_ASN_PRESENT
|
|
|
|
|
|
|
|
}
|
|
|
|
HRESULT CH323Ctrl::Init(IConfAdvise *pConfAdvise)
|
|
{
|
|
hrLast = hrSuccess;
|
|
|
|
if(!(m_pConfAdvise = pConfAdvise))
|
|
{
|
|
hrLast = CCO_E_INVALID_PARAM;
|
|
goto EXIT;
|
|
}
|
|
|
|
EXIT:
|
|
return hrLast;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::DeInit(IConfAdvise *pConfAdvise)
|
|
{
|
|
hrLast = hrSuccess;
|
|
if(m_pConfAdvise != pConfAdvise)
|
|
{
|
|
hrLast = CCO_E_INVALID_PARAM;
|
|
goto EXIT;
|
|
}
|
|
m_pConfAdvise = NULL;
|
|
|
|
EXIT:
|
|
return hrLast;
|
|
}
|
|
|
|
BOOL CH323Ctrl::IsAcceptingConference(LPVOID lpvConfID)
|
|
{
|
|
if(memcmp(lpvConfID, &m_ConferenceID, sizeof(m_ConferenceID))==0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
HRESULT CH323Ctrl::GetProtocolID(LPGUID lpPID)
|
|
{
|
|
if(!lpPID)
|
|
return CCO_E_INVALID_PARAM;
|
|
|
|
*lpPID = m_PID;
|
|
hrLast = hrSuccess;
|
|
return hrLast;
|
|
}
|
|
|
|
IH323Endpoint * CH323Ctrl::GetIConnIF()
|
|
{
|
|
if(!m_pConfAdvise)
|
|
return NULL;
|
|
return m_pConfAdvise->GetIConnIF();
|
|
}
|
|
|
|
STDMETHODIMP CH323Ctrl::GetVersionInfo(
|
|
PCC_VENDORINFO *ppLocalVendorInfo,
|
|
PCC_VENDORINFO *ppRemoteVendorInfo)
|
|
{
|
|
|
|
FX_ENTRY ("CH323Ctrl::GetVersionInfo");
|
|
if(!ppLocalVendorInfo || !ppRemoteVendorInfo)
|
|
{
|
|
return CCO_E_INVALID_PARAM;
|
|
}
|
|
*ppLocalVendorInfo = &m_VendorInfo;
|
|
*ppRemoteVendorInfo = &m_RemoteVendorInfo;
|
|
return hrSuccess;
|
|
}
|
|
|
|
|
|
|
|
CH323Ctrl::CH323Ctrl()
|
|
:m_hListen(0),
|
|
m_hConference(0),
|
|
m_hCall(0),
|
|
m_pRemoteAliases(NULL),
|
|
m_pRemoteAliasItem(NULL),
|
|
pwszPeerDisplayName(NULL),
|
|
pwszPeerAliasName(NULL),
|
|
m_bMultipointController(FALSE),
|
|
m_fLocalT120Cap(TRUE),
|
|
m_fRemoteT120Cap(FALSE),
|
|
hrLast(hrSuccess),
|
|
m_ChanFlags(0),
|
|
m_hCallCompleteCode(0),
|
|
m_pConfAdvise(NULL),
|
|
m_Phase( CCS_Idle ),
|
|
#ifdef BETA_2_ASN_PRESENT
|
|
m_fAvoidCrashingPDUs(FALSE),
|
|
#endif
|
|
|
|
uRef(1)
|
|
{
|
|
m_PID = PID_H323;
|
|
ZeroMemory(&m_ConferenceID,sizeof(m_ConferenceID));
|
|
ZeroMemory(&local_sin, sizeof(local_sin));
|
|
ZeroMemory(&remote_sin, sizeof(remote_sin));
|
|
ZeroMemory(&m_RemoteVendorInfo, sizeof(m_RemoteVendorInfo));
|
|
local_sin_len = sizeof(local_sin);
|
|
remote_sin_len = sizeof(remote_sin);
|
|
|
|
m_VendorInfo.bCountryCode = USA_H221_COUNTRY_CODE;
|
|
m_VendorInfo.bExtension = USA_H221_COUNTRY_EXTENSION;
|
|
m_VendorInfo.wManufacturerCode = MICROSOFT_H_221_MFG_CODE;
|
|
|
|
m_VendorInfo.pProductNumber = (PCC_OCTETSTRING)MemAlloc(sizeof(CC_OCTETSTRING)
|
|
+ sizeof(DefaultProductID));
|
|
if(m_VendorInfo.pProductNumber)
|
|
{
|
|
m_VendorInfo.pProductNumber->wOctetStringLength = sizeof(DefaultProductID);
|
|
m_VendorInfo.pProductNumber->pOctetString =
|
|
((BYTE *)m_VendorInfo.pProductNumber + sizeof(CC_OCTETSTRING));
|
|
memcpy(m_VendorInfo.pProductNumber->pOctetString,
|
|
DefaultProductID, sizeof(DefaultProductID));
|
|
}
|
|
|
|
m_VendorInfo.pVersionNumber = (PCC_OCTETSTRING)MemAlloc(sizeof(CC_OCTETSTRING)
|
|
+ sizeof(DefaultProductVersion));
|
|
if(m_VendorInfo.pVersionNumber)
|
|
{
|
|
m_VendorInfo.pVersionNumber->wOctetStringLength = sizeof(DefaultProductVersion);
|
|
m_VendorInfo.pVersionNumber->pOctetString =
|
|
((BYTE *)m_VendorInfo.pVersionNumber + sizeof(CC_OCTETSTRING));
|
|
memcpy(m_VendorInfo.pVersionNumber->pOctetString,
|
|
DefaultProductVersion, sizeof(DefaultProductVersion));
|
|
}
|
|
|
|
m_NonstandardData.bCountryCode = USA_H221_COUNTRY_CODE;
|
|
m_NonstandardData.bExtension = USA_H221_COUNTRY_EXTENSION;
|
|
m_NonstandardData.wManufacturerCode = MICROSOFT_H_221_MFG_CODE;
|
|
m_NonstandardData.sData.pOctetString = NULL;
|
|
m_NonstandardData.sData.wOctetStringLength = 0;
|
|
m_ParticipantList.wLength = 0;
|
|
m_ParticipantList.ParticipantInfoArray = NULL;
|
|
m_ConferenceAttributes.pParticipantList = &m_ParticipantList;
|
|
}
|
|
|
|
|
|
VOID CH323Ctrl ::ReleaseAllChannels()
|
|
{
|
|
ICtrlCommChan *pChan = NULL;
|
|
if (!m_ChannelList.IsEmpty())
|
|
{
|
|
while (!m_ChannelList.IsEmpty())
|
|
{
|
|
pChan = (ICtrlCommChan *) m_ChannelList.RemoveHead();
|
|
if(pChan)
|
|
{
|
|
pChan->EndControlSession();
|
|
pChan->Release();
|
|
pChan = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CH323Ctrl ::~CH323Ctrl()
|
|
{
|
|
Cleanup();
|
|
ReleaseAllChannels();
|
|
if(m_pRemoteAliases)
|
|
FreeTranslatedAliasList(m_pRemoteAliases);
|
|
if(pwszPeerDisplayName)
|
|
MemFree(pwszPeerDisplayName);
|
|
if(pwszPeerAliasName)
|
|
MemFree(pwszPeerAliasName);
|
|
if(m_pRemoteAliasItem)
|
|
MemFree(m_pRemoteAliasItem);
|
|
if(m_NonstandardData.sData.pOctetString)
|
|
MemFree(m_NonstandardData.sData.pOctetString);
|
|
if(m_VendorInfo.pProductNumber)
|
|
MemFree(m_VendorInfo.pProductNumber);
|
|
if(m_VendorInfo.pVersionNumber)
|
|
MemFree(m_VendorInfo.pVersionNumber);
|
|
if(m_RemoteVendorInfo.pProductNumber)
|
|
MemFree(m_RemoteVendorInfo.pProductNumber);
|
|
if(m_RemoteVendorInfo.pVersionNumber)
|
|
MemFree(m_RemoteVendorInfo.pVersionNumber);
|
|
}
|
|
|
|
|