976 lines
24 KiB
C++
976 lines
24 KiB
C++
|
/*
|
||
|
* File: connobj.cpp
|
||
|
*
|
||
|
* implementation of Microsoft Network Audio connection object.
|
||
|
*
|
||
|
*
|
||
|
*
|
||
|
* Revision History:
|
||
|
*
|
||
|
* 05/05/96 mikev created
|
||
|
* 08/04/96 philf added support for video
|
||
|
* 09/22/96 mikev dual call control protocols (H.323 & MSICCP)
|
||
|
* 10/14/96 mikev multiple channel support, property I/F
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#include "ctrlh323.h"
|
||
|
#include "strutil.h"
|
||
|
|
||
|
|
||
|
CREQ_RESPONSETYPE CConnection::FilterConnectionRequest(
|
||
|
LPIControlChannel lpControlChannel,
|
||
|
P_APP_CALL_SETUP_DATA pAppData)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::FilterConnectionRequest");
|
||
|
CREQ_RESPONSETYPE cr;
|
||
|
// validate lpControlChannel - this implementation sets it inside
|
||
|
// GetAcceptingObject()
|
||
|
if(m_pControlChannel != lpControlChannel)
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:bad param:my pChan:0x%08lX, param pChan:0x%08lX\r\n",
|
||
|
_fx_, m_pControlChannel, lpControlChannel));
|
||
|
hrLast = CADV_E_INVALID_PARAM;
|
||
|
return CRR_ERROR;
|
||
|
}
|
||
|
m_ConnectionState = CLS_Alerting;
|
||
|
cr = m_pH323CallControl->FilterConnectionRequest(this, pAppData);
|
||
|
switch (cr)
|
||
|
{
|
||
|
case CRR_ASYNC:
|
||
|
// m_ConnectionState = CLS_Alerting; // stays in this state
|
||
|
break;
|
||
|
case CRR_ACCEPT:
|
||
|
m_ConnectionState = CLS_Connecting;
|
||
|
break;
|
||
|
|
||
|
// set summary codes in reject cases
|
||
|
case CRR_BUSY:
|
||
|
m_ConnectionState = CLS_Idle;
|
||
|
SummaryCode(CCR_LOCAL_BUSY);
|
||
|
break;
|
||
|
case CRR_SECURITY_DENIED:
|
||
|
m_ConnectionState = CLS_Idle;
|
||
|
SummaryCode(CCR_LOCAL_SECURITY_DENIED);
|
||
|
break;
|
||
|
default:
|
||
|
case CRR_REJECT:
|
||
|
m_ConnectionState = CLS_Idle;
|
||
|
SummaryCode(CCR_LOCAL_REJECT);
|
||
|
break;
|
||
|
}
|
||
|
return(cr);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection::FindAcceptingObject(LPIControlChannel *lplpAcceptingObject,
|
||
|
LPVOID lpvConfID)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::FindAcceptingObject");
|
||
|
HRESULT hr = H323CC_E_CONNECTION_NOT_FOUND;
|
||
|
ULONG ulCount, uNumConnections;
|
||
|
CConnection **ppConnections = NULL;;
|
||
|
LPIControlChannel pCtlChan;
|
||
|
CConnection *pConnection;
|
||
|
|
||
|
if(!lplpAcceptingObject)
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:null lplpAcceptingObject\r\n",_fx_));
|
||
|
return CADV_E_INVALID_PARAM;
|
||
|
}
|
||
|
// zero out the output param
|
||
|
*lplpAcceptingObject = NULL;
|
||
|
hr = m_pH323CallControl->GetNumConnections(&uNumConnections);
|
||
|
if(!HR_SUCCEEDED(hr))
|
||
|
goto EXIT;
|
||
|
if(!uNumConnections)
|
||
|
{
|
||
|
// initialized value hr = H323CC_E_CONNECTION_NOT_FOUND;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
ppConnections = (CConnection **)MemAlloc(uNumConnections * (sizeof(IH323Endpoint * *)));
|
||
|
if(!ppConnections)
|
||
|
{
|
||
|
hr = H323CC_E_INSUFFICIENT_MEMORY;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
// get list of connections and query each one for matching conference ID
|
||
|
hr = m_pH323CallControl->GetConnobjArray(ppConnections, uNumConnections * (sizeof(IH323Endpoint * *)));
|
||
|
if(!HR_SUCCEEDED(hr))
|
||
|
goto EXIT;
|
||
|
|
||
|
for(ulCount=0;ulCount <uNumConnections;ulCount++)
|
||
|
{
|
||
|
pConnection = ppConnections[ulCount];
|
||
|
if(pConnection && (pCtlChan = pConnection->GetControlChannel())
|
||
|
&& pCtlChan->IsAcceptingConference(lpvConfID))
|
||
|
{
|
||
|
*lplpAcceptingObject = pCtlChan;
|
||
|
hr = hrSuccess;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EXIT:
|
||
|
if(ppConnections)
|
||
|
MemFree(ppConnections);
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection::GetAcceptingObject(LPIControlChannel *lplpAcceptingObject,
|
||
|
LPGUID pPID)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::GetAcceptingObject");
|
||
|
HRESULT hr;
|
||
|
CConnection *pNewConnection;
|
||
|
if(!lplpAcceptingObject)
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:null lplpAcceptingObject\r\n",_fx_));
|
||
|
return CADV_E_INVALID_PARAM;
|
||
|
}
|
||
|
// zero out the output param
|
||
|
*lplpAcceptingObject = NULL;
|
||
|
|
||
|
// create a connection object to accept the connection
|
||
|
hr = m_pH323CallControl->CreateConnection(&pNewConnection, *pPID);
|
||
|
if(HR_SUCCEEDED(hr))
|
||
|
{
|
||
|
*lplpAcceptingObject = pNewConnection->GetControlChannel();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:CreateConnection failed, hr=0x%08lx\r\n",_fx_, hr));
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// This is called by a comm channel. It is only called by a channel that is being
|
||
|
// opened, and only if that channel is not already associated with a control channel.
|
||
|
HRESULT CConnection::AddCommChannel (ICtrlCommChan *pChan)
|
||
|
{
|
||
|
GUID mid;
|
||
|
if(!m_fCapsReady)
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
hrLast = CONN_E_NOT_INITIALIZED; // need better error to indicate why
|
||
|
// (connection is not yet in a state to take new channels)
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
// re-initialize channel
|
||
|
hrLast = pChan->GetMediaType(&mid);
|
||
|
|
||
|
ASSERT(m_pH323ConfAdvise != NULL);
|
||
|
if(!pChan->Init(&mid, m_pH323ConfAdvise, TRUE))
|
||
|
{
|
||
|
hrLast = CONN_E_SYSTEM_ERROR;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
// non error case continues here
|
||
|
if(m_pControlChannel)
|
||
|
{
|
||
|
m_ChannelList.AddTail(pChan);
|
||
|
pChan->AddRef();
|
||
|
hrLast = m_pControlChannel->AddChannel(pChan, m_pCapObject);
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
EXIT:
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection::CreateCommChannel(LPGUID pMediaGuid, ICommChannel **ppICommChannel,
|
||
|
BOOL fSend)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::CreateCommChannel");
|
||
|
ICommChannel *pICommChannel = NULL;
|
||
|
ICtrlCommChan *pICtrlCommChannel = NULL;
|
||
|
|
||
|
if(!pMediaGuid || !ppICommChannel)
|
||
|
{
|
||
|
hrLast = CONN_E_INVALID_PARAM;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
DBG_SAVE_FILE_LINE
|
||
|
if(*pMediaGuid == MEDIA_TYPE_H323_T120)
|
||
|
{
|
||
|
if(!(pICommChannel = (ICommChannel *)new ImpT120Chan))
|
||
|
{
|
||
|
hrLast = CONN_E_OUT_OF_MEMORY;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
}
|
||
|
else if(!(pICommChannel = (ICommChannel *)new ImpICommChan))
|
||
|
{
|
||
|
hrLast = CONN_E_OUT_OF_MEMORY;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
hrLast = pICommChannel->QueryInterface(IID_ICtrlCommChannel, (void **)&pICtrlCommChannel);
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
{
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
ASSERT(m_pH323ConfAdvise != NULL);
|
||
|
if(!pICtrlCommChannel->Init(pMediaGuid, m_pH323ConfAdvise, fSend))
|
||
|
{
|
||
|
hrLast = CONN_E_SYSTEM_ERROR;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
// it's created via this connection, now associate it and this connection
|
||
|
if(m_pControlChannel)
|
||
|
{
|
||
|
m_ChannelList.AddTail(pICtrlCommChannel);
|
||
|
hrLast = m_pControlChannel->AddChannel(pICtrlCommChannel, m_pCapObject);
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
|
||
|
// in success case, the calling function gets the ICommChannel reference, and this
|
||
|
// object gets the ICtrlCommChan reference
|
||
|
*ppICommChannel = pICommChannel;
|
||
|
pICommChannel = NULL;
|
||
|
pICtrlCommChannel = NULL;
|
||
|
|
||
|
EXIT:
|
||
|
if(pICommChannel)
|
||
|
pICommChannel->Release();
|
||
|
if(pICtrlCommChannel)
|
||
|
pICtrlCommChannel->Release();
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
HRESULT CConnection:: ResolveFormats (LPGUID pMediaGuidArray, UINT uNumMedia,
|
||
|
PRES_PAIR pResOutput)
|
||
|
{
|
||
|
ASSERT(NULL !=m_pCapObject);
|
||
|
return (m_pCapObject->ResolveFormats(pMediaGuidArray, uNumMedia, pResOutput));
|
||
|
}
|
||
|
|
||
|
HRESULT CConnection::GetVersionInfo(PCC_VENDORINFO *ppLocalVendorInfo,
|
||
|
PCC_VENDORINFO *ppRemoteVendorInfo)
|
||
|
{
|
||
|
if(!m_pControlChannel)
|
||
|
return CONN_E_NOT_INITIALIZED;
|
||
|
|
||
|
return (m_pControlChannel->GetVersionInfo(ppLocalVendorInfo, ppRemoteVendorInfo));
|
||
|
}
|
||
|
|
||
|
VOID CConnection ::ReleaseAllChannels()
|
||
|
{
|
||
|
ICtrlCommChan *pChan = NULL;
|
||
|
while (!m_ChannelList.IsEmpty())
|
||
|
{
|
||
|
pChan = (ICtrlCommChan *) m_ChannelList.RemoveHead();
|
||
|
if(pChan)
|
||
|
{
|
||
|
pChan->Release();
|
||
|
pChan = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Implementation of IConfAdvise::OnControlEvent
|
||
|
//
|
||
|
// CAUTION: because Release() can be called by the registered event handler,
|
||
|
// any code path that accesses class instance data after a call to m_pH323ConfAdvise->CallEvent
|
||
|
// must AddRef() before the call, and Release() after all class instance data access
|
||
|
// is done. The DoControlNotification() helper method does this, but beware of
|
||
|
// cases where data is touched after a call to DoControlNotification();
|
||
|
//
|
||
|
HRESULT CConnection::OnControlEvent(DWORD dwEvent, LPVOID lpvData, LPIControlChannel lpControlObject)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::OnControlEvent");
|
||
|
DWORD dwStatus;
|
||
|
BOOL fPost = FALSE;
|
||
|
HRESULT hr=hrSuccess;
|
||
|
|
||
|
AddRef();
|
||
|
switch(dwEvent)
|
||
|
{
|
||
|
case CCEV_RINGING:
|
||
|
fPost = TRUE;
|
||
|
dwStatus = CONNECTION_PROCEEDING;
|
||
|
break;
|
||
|
case CCEV_CONNECTED:
|
||
|
fPost = TRUE;
|
||
|
dwStatus = CONNECTION_CONNECTED;
|
||
|
NewUserInfo((LPCTRL_USER_INFO)lpvData);
|
||
|
break;
|
||
|
case CCEV_CALLER_ID:
|
||
|
NewUserInfo((LPCTRL_USER_INFO)lpvData);
|
||
|
break;
|
||
|
case CCEV_CAPABILITIES_READY:
|
||
|
m_fCapsReady = TRUE;
|
||
|
break;
|
||
|
|
||
|
case CCEV_CHANNEL_REQUEST:
|
||
|
// another channel (besides the channels supplied by EnumChannels()) is being
|
||
|
// requested - we can't handle arbitrary channels yet.
|
||
|
ERRORMESSAGE(("%s, not handling CCEV_CHANNEL_REQUEST \r\n",_fx_));
|
||
|
hr = CADV_E_NOT_SUPPORTED;
|
||
|
goto out;
|
||
|
break;
|
||
|
|
||
|
case CCEV_DISCONNECTING:
|
||
|
//in the future architecture, this event will be the opportunity to
|
||
|
//cleanup channels
|
||
|
if(lpvData)
|
||
|
{
|
||
|
// keep summary code
|
||
|
SummaryCode((HRESULT) *((HRESULT *)lpvData));
|
||
|
}
|
||
|
Disconnect(CCR_UNKNOWN);
|
||
|
// IConnect doesn't yet define a "disconnecting" event, so don't propagate it
|
||
|
break;
|
||
|
case CCEV_REMOTE_DISCONNECTING:
|
||
|
if(lpvData)
|
||
|
{
|
||
|
SummaryCode((HRESULT) *((HRESULT *)lpvData));
|
||
|
}
|
||
|
// do notification before calling back into Disconnect, so the event
|
||
|
// notifications are posted in the correct order. This is one of
|
||
|
// the cases where Ref count protection is required.
|
||
|
AddRef();
|
||
|
DoControlNotification(CONNECTION_RECEIVED_DISCONNECT);
|
||
|
// opportunity to cleanup channels
|
||
|
Disconnect(CCR_UNKNOWN);
|
||
|
Release();
|
||
|
break;
|
||
|
case CCEV_DISCONNECTED:
|
||
|
fPost = TRUE;
|
||
|
m_ConnectionState = CLS_Idle;
|
||
|
dwStatus = CONNECTION_DISCONNECTED;
|
||
|
if(lpvData)
|
||
|
{
|
||
|
SummaryCode((HRESULT) *((HRESULT *)lpvData));
|
||
|
}
|
||
|
break;
|
||
|
case CCEV_ALL_CHANNELS_READY:
|
||
|
// all *mandatory* channels are open, but not necessarily
|
||
|
// all channels
|
||
|
m_ConnectionState = CLS_Inuse;
|
||
|
dwStatus = CONNECTION_READY;
|
||
|
fPost = TRUE;
|
||
|
break;
|
||
|
case CCEV_ACCEPT_INCOMPLETE:
|
||
|
if(lpvData)
|
||
|
{
|
||
|
// known problem is that control channel has already
|
||
|
// disconnected and may have notified of the disconnect first.
|
||
|
// This could be fixed, but it's not an issue because an incomplete
|
||
|
// accept is not made known to the UI, therefore the summary code
|
||
|
// is dust anyway.
|
||
|
SummaryCode((HRESULT) *((HRESULT *)lpvData));
|
||
|
}
|
||
|
if(lpControlObject && (m_pControlChannel == lpControlObject))
|
||
|
{
|
||
|
// remove interest in control channel events, then nuke it
|
||
|
m_pControlChannel->DeInit((IConfAdvise *) this);
|
||
|
m_pControlChannel->Release();
|
||
|
}
|
||
|
m_pControlChannel = NULL;
|
||
|
if(m_pH323CallControl)
|
||
|
{
|
||
|
m_pH323CallControl->RemoveConnection(this);
|
||
|
}
|
||
|
Release(); // release self - this is by design
|
||
|
|
||
|
break;
|
||
|
case CCEV_CALL_INCOMPLETE:
|
||
|
hr = OnCallIncomplete(lpControlObject, (lpvData)? ((DWORD) *((DWORD *)lpvData)) :0);
|
||
|
goto out;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
if(fPost)
|
||
|
DoControlNotification(dwStatus);
|
||
|
|
||
|
out:
|
||
|
Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CConnection::OnCallIncomplete (LPIControlChannel lpControlObject, HRESULT hIncompleteCode)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::OnCallIncomplete ");
|
||
|
// check the reason for incomplete call attempt (busy? rejected? nobody home?
|
||
|
HRESULT hSummary;
|
||
|
CloseAllChannels();
|
||
|
|
||
|
// map the protocol-specific (h.323, msiccp, sip, etc) code to the
|
||
|
// connection interface code
|
||
|
// test for gatekeeper admission reject
|
||
|
// FACILITY_GKIADMISSION
|
||
|
if(CUSTOM_FACILITY(hIncompleteCode) == FACILITY_GKIADMISSION)
|
||
|
{
|
||
|
// pass GK codes through intact
|
||
|
hSummary = hIncompleteCode;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (hIncompleteCode)
|
||
|
{
|
||
|
case CCCI_GK_NO_RESOURCES:
|
||
|
hSummary = CCR_GK_NO_RESOURCES;
|
||
|
break;
|
||
|
case CCCI_BUSY:
|
||
|
hSummary = CCR_REMOTE_BUSY;
|
||
|
break;
|
||
|
case CCCI_SECURITY_DENIED:
|
||
|
hSummary = CCR_REMOTE_SECURITY_DENIED;
|
||
|
break;
|
||
|
case CCCI_NO_ANSWER_TIMEOUT:
|
||
|
hSummary = CCR_NO_ANSWER_TIMEOUT;
|
||
|
break;
|
||
|
case CCCI_REJECTED:
|
||
|
hSummary = CCR_REMOTE_REJECTED;
|
||
|
break;
|
||
|
case CCCI_REMOTE_ERROR:
|
||
|
hSummary = CCR_REMOTE_SYSTEM_ERROR;
|
||
|
break;
|
||
|
case CCCI_LOCAL_ERROR:
|
||
|
hSummary = CCR_LOCAL_SYSTEM_ERROR;
|
||
|
break;
|
||
|
case CCCI_INCOMPATIBLE:
|
||
|
hSummary = CCR_LOCAL_PROTOCOL_ERROR;
|
||
|
break;
|
||
|
case CCCI_UNKNOWN:
|
||
|
hSummary = CCR_UNKNOWN;
|
||
|
default:
|
||
|
hSummary = CCR_UNKNOWN;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DEBUGMSG(ZONE_CONN,("%s: incomplete code = 0x%08lX\r\n",
|
||
|
_fx_, hIncompleteCode));
|
||
|
SummaryCode(hSummary);
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
VOID CConnection::NewUserInfo(LPCTRL_USER_INFO lpNewUserInfo)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::NewUserInfo");
|
||
|
|
||
|
if(!lpNewUserInfo || !lpNewUserInfo->dwCallerIDSize || !lpNewUserInfo->lpvCallerIDData)
|
||
|
return;
|
||
|
|
||
|
if(m_pUserInfo)
|
||
|
{
|
||
|
DEBUGMSG(ZONE_CONN,("%s:uninitialized m_pUserInfo (0x%08lX) or multiple notification \r\n",
|
||
|
_fx_, m_pUserInfo ));
|
||
|
//
|
||
|
if(!IsBadWritePtr((LPVOID)m_pUserInfo, m_pUserInfo->dwCallerIDSize + sizeof(CTRL_USER_INFO)))
|
||
|
{
|
||
|
// chances are it *is* a multiple notification and not an uninitialized
|
||
|
// variable. Ther may be some control channel protocols that *update* user
|
||
|
// information after connection or accepting, but that is pure speculation.
|
||
|
// the typical case is that caller ID is available before accepting, and
|
||
|
// it is resupplied in the subsequent "connected" notification. We're not
|
||
|
// wasting time realloc'ing and recopying it.
|
||
|
return;
|
||
|
}
|
||
|
// else fallout and overwrite it
|
||
|
}
|
||
|
// copy the structure and caller ID data
|
||
|
m_pUserInfo = (LPCTRL_USER_INFO)MemAlloc(lpNewUserInfo->dwCallerIDSize + sizeof(CTRL_USER_INFO));
|
||
|
|
||
|
if(m_pUserInfo)
|
||
|
{
|
||
|
m_pUserInfo->lpvRemoteProtocolInfo = NULL; // nothing touchess this later, but being safe anyway
|
||
|
m_pUserInfo->lpvLocalProtocolInfo = NULL;
|
||
|
|
||
|
m_pUserInfo->dwCallerIDSize = lpNewUserInfo->dwCallerIDSize;
|
||
|
// point past the structure
|
||
|
m_pUserInfo->lpvCallerIDData = ((BYTE *)m_pUserInfo) + sizeof(CTRL_USER_INFO);
|
||
|
memcpy(m_pUserInfo->lpvCallerIDData,
|
||
|
lpNewUserInfo->lpvCallerIDData,
|
||
|
m_pUserInfo->dwCallerIDSize);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:allocation of m_pUserInfo failed\r\n",_fx_));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Utility function for passing control channel events to the registered handler
|
||
|
// This is callable only by the control channel code running in the same thread
|
||
|
// as that which created the connection.
|
||
|
VOID CConnection::DoControlNotification(DWORD dwStatus)
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::DoControlNotification");
|
||
|
// issue notification to registered entity
|
||
|
if(m_pH323ConfAdvise)
|
||
|
{
|
||
|
AddRef(); // protect ourselves from calls back into methods that
|
||
|
// wind up in Release().
|
||
|
DEBUGMSG(ZONE_CONN,("%s:issuing notification 0x%08lX\r\n",_fx_, dwStatus));
|
||
|
m_pH323ConfAdvise->CallEvent((IH323Endpoint *)&m_ImpConnection, dwStatus);
|
||
|
Release();
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
CConnection::CConnection()
|
||
|
:m_pH323CallControl(NULL),
|
||
|
hrLast(hrSuccess),
|
||
|
next(NULL),
|
||
|
m_fCapsReady(FALSE),
|
||
|
m_ConnectionState(CLS_Idle),
|
||
|
m_pH323ConfAdvise(NULL),
|
||
|
m_pUserInfo(NULL),
|
||
|
m_pControlChannel(NULL),
|
||
|
m_pCapObject(NULL),
|
||
|
m_hSummaryCode(hrSuccess),
|
||
|
uRef(1)
|
||
|
{
|
||
|
m_ImpConnection.Init(this);
|
||
|
}
|
||
|
|
||
|
CConnection::~CConnection()
|
||
|
{
|
||
|
ReleaseAllChannels();
|
||
|
if(m_pH323CallControl)
|
||
|
m_pH323CallControl->RemoveConnection(this);
|
||
|
|
||
|
if(m_pCapObject)
|
||
|
m_pCapObject->Release();
|
||
|
// we really don't allocate much
|
||
|
if(m_pUserInfo)
|
||
|
MemFree(m_pUserInfo);
|
||
|
|
||
|
}
|
||
|
|
||
|
HRESULT CConnection::Init(CH323CallControl *pH323CallControl, GUID PIDofProtocolType)
|
||
|
{
|
||
|
FX_ENTRY(("CConnection::Init"));
|
||
|
hrLast = hrSuccess;
|
||
|
BOOL bAdvertise;
|
||
|
m_pH323CallControl = pH323CallControl;
|
||
|
GUID mid;
|
||
|
|
||
|
if(!pH323CallControl)
|
||
|
return CCO_E_INVALID_PARAM;
|
||
|
|
||
|
if(m_pControlChannel)
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
// don't cleanup in this case
|
||
|
return CONN_E_ALREADY_INITIALIZED;
|
||
|
}
|
||
|
|
||
|
if(PIDofProtocolType != PID_H323)
|
||
|
{
|
||
|
hrLast = CONN_E_INIT_FAILED;
|
||
|
goto ERROR_CLEANUP;
|
||
|
}
|
||
|
|
||
|
DBG_SAVE_FILE_LINE
|
||
|
if(!(m_pControlChannel = (LPIControlChannel) new CH323Ctrl))
|
||
|
{
|
||
|
hrLast = CONN_E_INIT_FAILED;
|
||
|
goto ERROR_CLEANUP;
|
||
|
}
|
||
|
|
||
|
DBG_SAVE_FILE_LINE
|
||
|
if(!m_pCapObject && !(m_pCapObject = new CapsCtl()))
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:cannot create capability resolver\r\n",_fx_));
|
||
|
hrLast = CONN_E_INIT_FAILED;
|
||
|
goto ERROR_CLEANUP;;
|
||
|
}
|
||
|
if(!m_pCapObject->Init())
|
||
|
{
|
||
|
ERRORMESSAGE(("%s:cannot init capability resolver\r\n",_fx_));
|
||
|
hrLast = CONN_E_INIT_FAILED;
|
||
|
goto ERROR_CLEANUP;
|
||
|
}
|
||
|
|
||
|
bAdvertise = ((g_capFlags & CAPFLAGS_AV_STREAMS) != 0);
|
||
|
mid = MEDIA_TYPE_H323AUDIO;
|
||
|
hrLast = m_pCapObject->EnableMediaType(bAdvertise, &mid);
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
goto ERROR_CLEANUP;
|
||
|
|
||
|
bAdvertise = ((g_capFlags & CAPFLAGS_AV_STREAMS) != 0);
|
||
|
mid = MEDIA_TYPE_H323VIDEO;
|
||
|
hrLast = m_pCapObject->EnableMediaType(bAdvertise, &mid);
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
goto ERROR_CLEANUP;
|
||
|
|
||
|
hrLast = m_pControlChannel->Init((IConfAdvise *) this);
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
goto ERROR_CLEANUP;
|
||
|
|
||
|
return hrLast;
|
||
|
|
||
|
ERROR_CLEANUP:
|
||
|
ERRORMESSAGE(("%s:ERROR_CLEANUP\r\n",_fx_));
|
||
|
|
||
|
if(m_pControlChannel)
|
||
|
m_pControlChannel->Release();
|
||
|
if(m_pCapObject)
|
||
|
m_pCapObject->Release();
|
||
|
m_pControlChannel = NULL;
|
||
|
m_pCapObject = NULL;
|
||
|
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
BOOL CConnection::ListenOn(PORT port)
|
||
|
{
|
||
|
if(!m_pControlChannel)
|
||
|
{
|
||
|
hrLast = H323CC_E_NOT_INITIALIZED;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
hrLast = m_pControlChannel->ListenOn(port);
|
||
|
EXIT:
|
||
|
return((HR_SUCCEEDED(hrLast))?TRUE:FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
// start the asynchronous stuff that will instantiate a control channel
|
||
|
HRESULT CConnection::PlaceCall (BOOL bUseGKResolution, PSOCKADDR_IN pCallAddr,
|
||
|
P_H323ALIASLIST pDestinationAliases, P_H323ALIASLIST pExtraAliases,
|
||
|
LPCWSTR pCalledPartyNumber, P_APP_CALL_SETUP_DATA pAppData)
|
||
|
{
|
||
|
if(m_ConnectionState != CLS_Idle)
|
||
|
return CONN_E_NOT_IDLE;
|
||
|
|
||
|
m_fCapsReady = FALSE;
|
||
|
// reset summary code
|
||
|
m_hSummaryCode = CCR_INVALID_REASON;
|
||
|
|
||
|
hrLast = m_pH323CallControl->GetGKCallPermission();
|
||
|
if(!HR_SUCCEEDED(hrLast))
|
||
|
{
|
||
|
m_hSummaryCode = hrLast;
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
hrLast = m_pControlChannel->PlaceCall (bUseGKResolution, pCallAddr,
|
||
|
pDestinationAliases, pExtraAliases,
|
||
|
pCalledPartyNumber, pAppData);
|
||
|
|
||
|
if(HR_SUCCEEDED(hrLast))
|
||
|
m_ConnectionState = CLS_Connecting;
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection::AcceptRejectConnection(CREQ_RESPONSETYPE Response)
|
||
|
{
|
||
|
if(Response == CRR_ACCEPT)
|
||
|
{
|
||
|
m_ConnectionState = CLS_Connecting;
|
||
|
m_fCapsReady = FALSE;
|
||
|
// reset summary code
|
||
|
m_hSummaryCode = CCR_INVALID_REASON;
|
||
|
}
|
||
|
return m_pControlChannel->AsyncAcceptRejectCall(Response);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection::SetAdviseInterface(IH323ConfAdvise *pH323ConfAdvise)
|
||
|
{
|
||
|
ASSERT(pH323ConfAdvise != NULL);
|
||
|
if(!pH323ConfAdvise)
|
||
|
{
|
||
|
return CONN_E_INVALID_PARAM;
|
||
|
}
|
||
|
m_pH323ConfAdvise = pH323ConfAdvise;
|
||
|
//EXIT:
|
||
|
return hrSuccess;
|
||
|
}
|
||
|
|
||
|
HRESULT CConnection::ClearAdviseInterface()
|
||
|
{
|
||
|
m_pH323ConfAdvise = NULL;
|
||
|
return hrSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
// LOOKLOOK - the H323 control channel needs to get the combined cap object
|
||
|
// implementation of IConfAdvise::GetCapResolver()
|
||
|
HRESULT CConnection::GetCapResolver(LPVOID *lplpCapObject, GUID CapType)
|
||
|
{
|
||
|
if(!lplpCapObject)
|
||
|
return CONN_E_INVALID_PARAM;
|
||
|
|
||
|
if(!m_pH323CallControl || !m_pCapObject)
|
||
|
return CONN_E_NOT_INITIALIZED;
|
||
|
|
||
|
if(CapType == OID_CAP_ACM_TO_H323)
|
||
|
{
|
||
|
*lplpCapObject = m_pCapObject;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return CONN_E_INVALID_PARAM;
|
||
|
}
|
||
|
return hrSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection::GetState(ConnectStateType *pState)
|
||
|
{
|
||
|
HRESULT hResult = hrSuccess;
|
||
|
if(!pState)
|
||
|
{
|
||
|
hResult = CONN_E_INVALID_PARAM;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
*pState = m_ConnectionState;
|
||
|
EXIT:
|
||
|
return hResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IConfAdvise::GetUserDisplayName()
|
||
|
LPWSTR CConnection::GetUserDisplayName()
|
||
|
{
|
||
|
if(!m_pH323CallControl)
|
||
|
return NULL;
|
||
|
return m_pH323CallControl->GetUserDisplayName();
|
||
|
}
|
||
|
PCC_ALIASITEM CConnection::GetUserDisplayAlias()
|
||
|
{
|
||
|
if(!m_pH323CallControl)
|
||
|
return NULL;
|
||
|
return m_pH323CallControl->GetUserDisplayAlias();
|
||
|
}
|
||
|
PCC_ALIASNAMES CConnection:: GetUserAliases()
|
||
|
{
|
||
|
if(!m_pH323CallControl)
|
||
|
return NULL;
|
||
|
return m_pH323CallControl->GetUserAliases();
|
||
|
}
|
||
|
HRESULT CConnection::GetLocalPort(PORT *lpPort)
|
||
|
{
|
||
|
if(!m_pControlChannel)
|
||
|
return CONN_E_NOT_INITIALIZED;
|
||
|
|
||
|
return m_pControlChannel->GetLocalPort(lpPort);
|
||
|
}
|
||
|
HRESULT CConnection::GetRemoteUserName(LPWSTR lpwszName, UINT uSize)
|
||
|
{
|
||
|
|
||
|
if(!lpwszName)
|
||
|
{
|
||
|
hrLast = MakeResult(CONN_E_INVALID_PARAM);
|
||
|
goto EXIT;
|
||
|
}
|
||
|
if(!m_pUserInfo)
|
||
|
{
|
||
|
// LOOKLOOK - need CONN_E_UNAVAILABLE or something
|
||
|
hrLast = MakeResult(CONN_E_INVALID_PARAM);
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
LStrCpyNW((LPWSTR)lpwszName,(LPWSTR)m_pUserInfo->lpvCallerIDData, uSize);
|
||
|
hrLast = hrSuccess;
|
||
|
EXIT:
|
||
|
return hrLast;
|
||
|
}
|
||
|
HRESULT CConnection::GetRemoteUserAddr(PSOCKADDR_IN psinUser)
|
||
|
{
|
||
|
PSOCKADDR_IN psin = NULL;
|
||
|
if(!m_pControlChannel)
|
||
|
return CONN_E_NOT_INITIALIZED;
|
||
|
|
||
|
if(psinUser)
|
||
|
{ // get ptr to address, then copy it
|
||
|
hrLast = m_pControlChannel->GetRemoteAddress(&psin);
|
||
|
if(HR_SUCCEEDED(hrLast) && psin)
|
||
|
{
|
||
|
*psinUser = *psin;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hrLast = H323CC_E_INVALID_PARAM;
|
||
|
}
|
||
|
//EXIT:
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConnection ::Disconnect()
|
||
|
{
|
||
|
SummaryCode(CCR_LOCAL_DISCONNECT);
|
||
|
Disconnect(CCR_LOCAL_DISCONNECT);
|
||
|
return hrSuccess;
|
||
|
}
|
||
|
|
||
|
HRESULT CConnection::CloseAllChannels()
|
||
|
{
|
||
|
ICtrlCommChan *pChan = NULL;
|
||
|
HRESULT hr; // temp return value so error code does not get overwritten
|
||
|
FX_ENTRY ("CConnection::CloseAllChannels");
|
||
|
|
||
|
// This doesn't actually cause channel close PDU's to be sent. It only
|
||
|
// shuts off all streams associated with all channels.
|
||
|
while (!m_ChannelList.IsEmpty())
|
||
|
{
|
||
|
pChan = (ICtrlCommChan *) m_ChannelList.RemoveHead();
|
||
|
if(pChan)
|
||
|
{
|
||
|
hr = pChan->OnChannelClose(CHANNEL_CLOSED);
|
||
|
if(!HR_SUCCEEDED(hr))
|
||
|
hrLast = hr;
|
||
|
hr = pChan->EndControlSession();
|
||
|
if(!HR_SUCCEEDED(hr))
|
||
|
hrLast = hr;
|
||
|
pChan->Release();
|
||
|
}
|
||
|
}
|
||
|
return hrLast;
|
||
|
}
|
||
|
|
||
|
VOID CConnection::Disconnect(DWORD dwResponse)
|
||
|
{
|
||
|
AddRef(); // prevent releasing while handling disconnect events
|
||
|
if(!m_pControlChannel)
|
||
|
{
|
||
|
m_ConnectionState = CLS_Idle;
|
||
|
goto EXIT;
|
||
|
}
|
||
|
|
||
|
if((m_ConnectionState == CLS_Disconnecting)
|
||
|
|| (m_ConnectionState == CLS_Idle))
|
||
|
{
|
||
|
goto EXIT;
|
||
|
}
|
||
|
m_ConnectionState = CLS_Disconnecting;
|
||
|
|
||
|
// CloseAllChannels() forces the action that would be taken when all
|
||
|
// channels are closed via call control. Anal channel cleanup is not
|
||
|
// implemented on disconnect- CloseAllChannels() turns off all streaming,
|
||
|
// then we just end the session. It takes too long to go through the
|
||
|
// protocol overhead of closing & acking channel close, and it's legal in
|
||
|
// H.323 to end the session. Ending the session implies channel closure
|
||
|
// for all channels.
|
||
|
//
|
||
|
|
||
|
CloseAllChannels();
|
||
|
|
||
|
// this call can result in callbacks to the UI, which can result in
|
||
|
// calls back in, which results in releasing the object. If we're
|
||
|
// about to go in, we need to be sure we can get back out, so AddRef()
|
||
|
m_pControlChannel->AddRef();
|
||
|
m_pControlChannel->Disconnect(dwResponse);
|
||
|
m_pControlChannel->Release();
|
||
|
EXIT:
|
||
|
Release();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
STDMETHODIMP CConnection::QueryInterface( REFIID iid, void ** ppvObject)
|
||
|
{
|
||
|
|
||
|
HRESULT hr = E_NOINTERFACE;
|
||
|
if(!ppvObject)
|
||
|
return hr;
|
||
|
|
||
|
*ppvObject = 0;
|
||
|
if((iid == IID_IPhoneConnection)
|
||
|
|| (iid == IID_IUnknown)) // satisfy symmetric property of QI
|
||
|
{
|
||
|
*ppvObject = this;
|
||
|
hr = hrSuccess;
|
||
|
AddRef();
|
||
|
}
|
||
|
else if(iid == IID_IConfAdvise)
|
||
|
{
|
||
|
*ppvObject = (IConfAdvise *)this;
|
||
|
hr = hrSuccess;
|
||
|
AddRef();
|
||
|
}
|
||
|
else if((iid == IID_IAppAudioCap ) && m_pCapObject)
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
hr = m_pCapObject->QueryInterface(iid, ppvObject);
|
||
|
}
|
||
|
else if((iid == IID_IAppVidCap ) && m_pCapObject)
|
||
|
{
|
||
|
/// ASSERT(0); CVideoProp still uses this
|
||
|
hr = m_pCapObject->QueryInterface(iid, ppvObject);
|
||
|
}
|
||
|
else if((iid == IID_IDualPubCap) && m_pCapObject)
|
||
|
{
|
||
|
ASSERT(0);
|
||
|
hr = m_pCapObject->QueryInterface(iid, ppvObject);
|
||
|
}
|
||
|
return (hr);
|
||
|
}
|
||
|
|
||
|
ULONG CConnection::AddRef()
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::AddRef");
|
||
|
uRef++;
|
||
|
DEBUGMSG(ZONE_REFCOUNT,("%s:(0x%08lX)->AddRef() uRef = 0x%08lX\r\n",_fx_, this, uRef ));
|
||
|
return uRef;
|
||
|
}
|
||
|
|
||
|
ULONG CConnection::Release()
|
||
|
{
|
||
|
FX_ENTRY ("CConnection::Release");
|
||
|
uRef--;
|
||
|
if(uRef == 0)
|
||
|
{
|
||
|
DEBUGMSG(ZONE_CONN,("%s:(0x%08lX)->Releasing in state:%d\r\n",_fx_, this, m_ConnectionState));
|
||
|
|
||
|
// remove our interest in the control channel
|
||
|
if(m_pControlChannel)
|
||
|
{
|
||
|
hrLast = m_pControlChannel->DeInit((IConfAdvise *) this);
|
||
|
m_pControlChannel->Release();
|
||
|
}
|
||
|
|
||
|
// m_pControlChannel = NULL;
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGMSG(ZONE_REFCOUNT,("%s:(0x%08lX)->Release() uRef = 0x%08lX\r\n",_fx_, this, uRef ));
|
||
|
return uRef;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CConnection::GetSummaryCode(VOID)
|
||
|
{
|
||
|
return m_hSummaryCode;
|
||
|
}
|
||
|
VOID CConnection::SummaryCode(HRESULT hCode)
|
||
|
{
|
||
|
// assign code only if it has not yet been assigned
|
||
|
if(m_hSummaryCode != CCR_INVALID_REASON)
|
||
|
return;
|
||
|
m_hSummaryCode = hCode;
|
||
|
}
|