WindowsXP-SP1/enduser/netmeeting/core/icall.cpp

2004 lines
47 KiB
C++

// File: icall.cpp
#include "precomp.h"
#include "icall.h"
#include "rostinfo.h"
#include "imanager.h"
#include "mperror.h"
#include "nmremote.h"
#include "util.h"
typedef struct
{
BOOL fMCU;
PWSTR * pwszConfNames;
BSTR * pbstrConfToJoin;
} REMOTE_CONFERENCE;
typedef struct
{
BSTR bstrConference;
BSTR *pbstrPassword;
PBYTE pbRemoteCred;
DWORD cbRemoteCred;
BOOL fIsService;
} REMOTE_PASSWORD;
HRESULT OnNotifyCallError(IUnknown *pCallNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyRemoteConference(IUnknown *pCallNotify, PVOID pv, REFIID riid);
static HRESULT OnNotifyRemotePassword(IUnknown *pCallNotify, PVOID pv, REFIID riid);
static const IID * g_apiidCP[] =
{
{&IID_INmCallNotify},
{&IID_INmCallNotify2}
};
// String Functions
inline VOID FreeBstr(BSTR *pbstr)
{
if (NULL != pbstr)
{
SysFreeString(*pbstr);
*pbstr = NULL;
}
}
/* P S Z A L L O C */
/*-------------------------------------------------------------------------
%%Function: PszAlloc
-------------------------------------------------------------------------*/
LPTSTR PszAlloc(LPCTSTR pszSrc)
{
if (NULL == pszSrc)
return NULL;
LPTSTR pszDest = new TCHAR[lstrlen(pszSrc) + 1];
if (NULL != pszDest)
{
lstrcpy(pszDest, pszSrc);
}
return pszDest;
}
COutgoingCall::COutgoingCall(CConfObject* pco,
DWORD dwFlags, NM_ADDR_TYPE addrType,
BSTR bstrName, BSTR bstrDest, BSTR bstrAlias,
BSTR bstrConference, BSTR bstrPassword, BSTR bstrUserString) :
CConnectionPointContainer(g_apiidCP, ARRAY_ELEMENTS(g_apiidCP)),
m_pConfObject (pco),
m_addrType (addrType),
m_dwFlags (dwFlags),
m_bstrName (SysAllocString(bstrName)),
m_bstrAlias (SysAllocString(bstrAlias)),
m_bstrConfToJoin (SysAllocString(bstrConference)),
m_bstrPassword (SysAllocString(bstrPassword)),
m_bstrUserString (SysAllocString(bstrUserString)),
m_hRequest (NULL),
m_pH323Connection (NULL),
m_fCanceled (FALSE),
m_cnResult (CN_RC_NOERROR),
m_cnState (CNS_IDLE),
m_fService (FALSE)
{
m_pszAddr = PszAlloc(CUSTRING(bstrDest));
DbgMsg(iZONE_OBJECTS, "Obj: %08X created COutgoingCall", this);
}
COutgoingCall::~COutgoingCall()
{
delete m_pszAddr;
FreeBstr(&m_bstrName);
FreeBstr(&m_bstrAlias);
FreeBstr(&m_bstrConfToJoin);
FreeBstr(&m_bstrPassword);
FreeBstr(&m_bstrUserString);
ASSERT(NULL == m_pH323Connection);
DbgMsg(iZONE_OBJECTS, "Obj: %08X destroyed COutgoingCall", this);
}
BOOL COutgoingCall::MatchActiveCallee(LPCTSTR pszAddr, BSTR bstrAlias, BSTR bstrConference)
{
return ((0 == lstrcmp(pszAddr, m_pszAddr)) &&
(0 == UnicodeCompare(bstrAlias, m_bstrAlias)));
}
/* P L A C E C A L L */
/*-------------------------------------------------------------------------
%%Function: PlaceCall
-------------------------------------------------------------------------*/
VOID COutgoingCall::PlaceCall(void)
{
DebugEntry(COutgoingCall::PlaceCall);
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
ASSERT(NULL != pOprahNCUI);
SetCallState(CNS_SEARCHING);
if ((CRPCF_H323CC & m_dwFlags) && (NULL != g_pH323UI))
{
SetCallState(CNS_CONNECTING_H323);
// Start placing the H.323 call:
CNSTATUS cnResult = ConnectH323();
if (CN_RC_NOERROR != cnResult)
{
m_cnResult = cnResult;
SetCallState(CNS_COMPLETE);
}
}
else if ((CRPCF_DATA & m_dwFlags) && (NULL != g_pNodeController))
{
// Start placing the T.120 call
CNSTATUS cnResult = StartT120Call();
if (CN_RC_NOERROR != cnResult)
{
m_cnResult = cnResult;
SetCallState(CNS_COMPLETE);
}
}
else
{
m_cnResult = CN_RC_TRANSPORT_FAILURE;
SetCallState(CNS_COMPLETE);
}
DebugExitVOID(COutgoingCall::PlaceCall);
}
CNSTATUS COutgoingCall::ConnectH323()
{
DBGENTRY(COutgoingCall::ConnectH323);
H323ALIASLIST AliasList;
H323ALIASNAME AliasName;
P_H323ALIASLIST pAliasList = &AliasList;
SOCKADDR_IN sin;
LPCWSTR pcwszPhone = NULL;
CNSTATUS cnResult = CN_RC_NOERROR;
if (NULL == g_pH323UI)
return cnResult;
AliasName.aType = AT_H323_ID;
AliasName.lpwData = m_bstrAlias;
AliasName.wDataLength = (WORD)SysStringLen(m_bstrAlias);// # of unicode chars, w/o NULL terminator
AliasList.wCount = 1;
AliasList.pItems = &AliasName;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(m_pszAddr);
switch (m_addrType)
{
case NM_ADDR_PSTN:
case NM_ADDR_H323_GATEWAY:
case NM_ADDR_ALIAS_E164:
// overide alias type and phone number
AliasName.aType = AT_H323_E164;
pcwszPhone = m_bstrAlias;
break;
case NM_ADDR_IP:
case NM_ADDR_MACHINENAME:
// overide alias list
pAliasList = NULL;
break;
case NM_ADDR_ALIAS_ID:
case NM_ADDR_ULS:
default:
break;
}
if (INADDR_NONE != sin.sin_addr.s_addr)
{
HRESULT hr;
ASSERT(g_pH323UI);
DWORD dwUserData = 0;
APP_CALL_SETUP_DATA acsd;
P_APP_CALL_SETUP_DATA pacsd = NULL;
BYTE *pbUserData = NULL;
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
ASSERT(NULL !=pOprahNCUI);
if (CRPCF_DATA & m_dwFlags)
{
if (CRPCF_SECURE & m_dwFlags)
{
dwUserData |= H323UDF_SECURE;
}
if (m_pConfObject->IsConferenceActive())
{
dwUserData |= H323UDF_INVITE;
}
else if (m_bstrConfToJoin != NULL)
{
dwUserData |= H323UDF_JOIN;
}
else
{
dwUserData |= H323UDF_INVITE | H323UDF_JOIN;
}
}
if (CRPCF_AUDIO & m_dwFlags)
{
dwUserData |= H323UDF_AUDIO;
}
if (CRPCF_VIDEO & m_dwFlags)
{
dwUserData |= H323UDF_VIDEO;
}
CRosterInfo ri;
PVOID pvRosterData;
UINT cbRosterData = 0;
if (pOprahNCUI->GetULSName(&ri))
{
if (FAILED(ri.Save(&pvRosterData, &cbRosterData)))
{
cbRosterData = 0;
}
}
UINT cbUserString = 0;
if (m_bstrUserString)
{
cbUserString = SysStringByteLen(m_bstrUserString);
}
DWORD dwTotalSize = sizeof(DWORD);
if (cbRosterData)
{
dwTotalSize += sizeof(GUID) + sizeof(DWORD) + cbRosterData;
}
if (cbUserString)
{
dwTotalSize += sizeof(GUID) + sizeof(DWORD) + cbUserString;
}
dwTotalSize += sizeof(GUID) + sizeof(DWORD) + sizeof(GUID);
pbUserData = new BYTE [dwTotalSize];
if (NULL != pbUserData)
{
BYTE *pb = pbUserData;
// H323UDF_ first
*((DWORD*)pb) = dwUserData;
pb += sizeof(DWORD);
if(cbRosterData)
{
// RosterInfo GUID next
*((GUID*)pb) = g_csguidRostInfo,
pb += sizeof(GUID);
// then RosterInfo size (a DWORD)
*((DWORD*)pb) = cbRosterData;
pb += sizeof(DWORD);
// then RosterInfo data
CopyMemory(pb, pvRosterData, cbRosterData);
pb += cbRosterData;
}
if(cbUserString)
{
// string GUID next
*((GUID*)pb) = g_csguidUserString,
pb += sizeof(GUID);
// then string size (a DWORD)
*((DWORD*)pb) = cbUserString;
pb += sizeof(DWORD);
// then string data
CopyMemory(pb, m_bstrUserString, cbUserString);
pb += cbUserString;
}
{
// string GUID next
*((GUID*)pb) = g_csguidNodeIdTag,
pb += sizeof(GUID);
// then string size (a DWORD)
*((DWORD*)pb) = sizeof(GUID);
pb += sizeof(DWORD);
// then GUID data
*(GUID*)pb = g_guidLocalNodeId;
pb += sizeof(GUID);
}
acsd.dwDataSize = dwTotalSize;
acsd.lpData = pbUserData;
pacsd = &acsd;
}
sin.sin_port = htons(H323_PORT);
hr = Connect(&sin, pAliasList, pcwszPhone, pacsd, PID_H323, &m_pH323Connection);
delete pbUserData;
if (SUCCEEDED(hr))
{
TRACE_OUT(("COutgoingCall - Connect() succeeded!"));
cnResult = CN_RC_NOERROR;
}
else
{
WARNING_OUT(("COutgoingCall - Connect() failed!"));
if (CONN_E_GK_NOT_REGISTERED == hr)
{
cnResult = CN_RC_GK_NOT_REGISTERED;
}
else
{
cnResult = CN_RC_AUDIO_CONNECT_FAILED;
}
}
}
else
{
WARNING_OUT(("COutgoingCall - inet_addr failed!"));
cnResult = CN_RC_NAME_RESOLUTION_FAILED;
}
return cnResult;
}
HRESULT COutgoingCall::Connect( PSOCKADDR_IN psin,
P_H323ALIASLIST pAliasList,
LPCWSTR pcwszPSTNAddress,
P_APP_CALL_SETUP_DATA lpvMNMData,
GUID PIDofProtocolType,
IH323Endpoint * *ppConnection)
{
DebugEntry(CH323UI::Connect);
HRESULT hr = E_ACCESSDENIED;
*ppConnection = NULL;
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
ASSERT(NULL != pOprahNCUI);
IH323CallControl * pH323CallControl = g_pH323UI->GetH323CallControl();
// create the connection
IH323Endpoint* pConn = NULL;
ASSERT(pH323CallControl);
hr = pH323CallControl->CreateConnection(&pConn, PIDofProtocolType);
if (FAILED(hr) || (NULL == pConn))
{
ERROR_OUT(("Could not create a connection, hr=0x%lx", hr));
}
else
{
// if the following operations fail, we still can delete this conn obj
*ppConnection = pConn;
if (!pOprahNCUI->AcquireAV(pConn))
{
// we did not get AV, clear out the flags
m_dwFlags &= ~(CRPCF_AUDIO | CRPCF_VIDEO);
}
hr = pConn->SetAdviseInterface (pOprahNCUI);
ASSERT(SUCCEEDED(hr));
// if we are using an alias, tell the Gatekeeper to resolve it
BOOL fUseGKResolution = (NULL != pAliasList);
// start the connection
hr = pConn->PlaceCall (fUseGKResolution, psin, pAliasList, NULL, pcwszPSTNAddress, lpvMNMData);
if (FAILED(hr))
{
// ERROR_OUT(("COutgoingCall::Connect: couldn't StartConnection, hr=0x%lx", hr));
if(hr == CONN_E_GK_NOT_REGISTERED)
{
WARNING_OUT(("COutgoingCall::Connect: not registered. Do you want to re-register..., hr=0x%lx", hr));
}
*ppConnection = NULL;
pConn->Release();
pOprahNCUI->ReleaseAV(pConn);
}
}
DebugExitULONG(CH323UI::Connect, hr);
return hr;
}
BOOL COutgoingCall::OnConferenceEnded()
{
DebugEntry(COutgoingCall::OnConferenceEnded);
BOOL bRet = FALSE;
switch (m_cnState)
{
case CNS_INVITING_REMOTE:
{
TRACE_OUT(("COutgoingCall (calling) rec. UNEXPECTED ConfEnded event"));
SetCallState(CNS_COMPLETE);
bRet = TRUE;
break;
}
case CNS_JOINING_REMOTE:
{
// JoinConference failed!
TRACE_OUT(("COutgoingCall (joining) received ConferenceEnded event"));
m_cnResult = CN_RC_CONFERENCE_JOIN_DENIED;
SetCallState(CNS_COMPLETE);
bRet = TRUE;
break;
}
case CNS_TERMINATING_AFTER_INVITE:
{
TRACE_OUT(("COutgoingCall (terminating after invite) received ConferenceEnded event"));
SetCallState(CNS_QUERYING_REMOTE_AFTER_INVITE);
ASSERT(g_pNodeController);
HRESULT hr = g_pNodeController->QueryRemote(this, m_pszAddr,
m_pConfObject->IsConfObjSecure(),
m_pConfObject->IsConferenceActive());
if (S_OK != hr)
{
m_cnResult = CN_RC_QUERY_FAILED;
SetCallState(CNS_COMPLETE);
}
bRet = TRUE;
break;
}
default:
{
WARNING_OUT(("COutgoingCall received unexpected ConfEnded event"));
}
}
DebugExitBOOL(COutgoingCall::OnConferenceEnded, bRet);
return bRet;
}
BOOL COutgoingCall::OnInviteResult(HRESULT ncsResult, UINT uNodeID)
{
DebugEntry(COutgoingCall::OnInviteResult);
BOOL bRet = TRUE;
ASSERT(CNS_INVITING_REMOTE == m_cnState);
TRACE_OUT(("COutgoingCall (calling) received InviteResult event"));
// Clear the current request handle
m_hRequest = NULL;
if (0 == ncsResult)
{
SetCallState(CNS_COMPLETE);
if (m_pH323Connection)
{
m_pConfObject->OnT120Connected(m_pH323Connection, uNodeID);
}
}
else
{
if (UI_RC_USER_REJECTED == ncsResult)
{
SetCallState(CNS_TERMINATING_AFTER_INVITE);
// Issue "soft" leave attempt (to allow auto-terminate)
ASSERT(m_pConfObject);
if (S_OK != m_pConfObject->LeaveConference(FALSE))
{
m_cnResult = CN_RC_CONFERENCE_INVITE_DENIED;
SetCallState(CNS_COMPLETE);
}
}
else
{
// make sure that we are not recieving this notification due to
// the conference going away
ASSERT(m_pConfObject);
if (CS_GOING_DOWN != m_pConfObject->GetT120State())
{
TRACE_OUT(("COutgoingCall - invite failed / couldn't connect -> leaving"));
m_cnResult = CN_RC_INVITE_FAILED;
SetCallState(CNS_COMPLETE);
// Issue "soft" leave attempt (to allow auto-terminate)
ASSERT(m_pConfObject);
m_pConfObject->LeaveConference(FALSE);
}
}
}
DebugExitBOOL(COutgoingCall::OnInviteResult, bRet);
return bRet;
}
BOOL COutgoingCall::OnQueryRemoteResult(HRESULT ncsResult,
BOOL fMCU,
PWSTR pwszConfNames[],
PT120PRODUCTVERSION pVersion,
PWSTR pwszConfDescriptors[])
{
DebugEntry(COutgoingCall::OnQueryRemoteResult);
ASSERT ((CNS_QUERYING_REMOTE == m_cnState) ||
(CNS_QUERYING_REMOTE_AFTER_INVITE == m_cnState));
ASSERT (NULL == m_bstrConfToJoin);
if (SUCCEEDED(ncsResult))
{
BOOL fRemoteInConf = FALSE;
if ((NULL != pwszConfNames) && (NULL != pwszConfNames[0]))
{
fRemoteInConf = TRUE;
}
m_fService = FALSE;
if (fRemoteInConf && (NULL != pwszConfDescriptors) && (NULL != pwszConfDescriptors[0]))
{
if (0 == UnicodeCompare(pwszConfDescriptors[0],RDS_CONFERENCE_DESCRIPTOR))
{
m_fService = TRUE;
}
}
if (m_pConfObject->IsConferenceActive())
{
if (fMCU)
{
TRACE_OUT(("COutgoingCall - QR ok, but is MCU -> complete"));
m_cnResult = CN_RC_CANT_INVITE_MCU;
}
else if (fRemoteInConf)
{
TRACE_OUT(("COutgoingCall - QR ok, but callee is in a conference"));
m_cnResult = CN_RC_INVITE_DENIED_REMOTE_IN_CONF;
}
else
{
if (CNS_QUERYING_REMOTE_AFTER_INVITE == m_cnState)
{
m_cnResult = CN_RC_CONFERENCE_INVITE_DENIED;
SetCallState(CNS_COMPLETE);
}
else
{
SetCallState(CNS_INVITING_REMOTE);
HRESULT hr = m_pConfObject->InviteConference(m_pszAddr, m_bstrUserString, &m_hRequest);
if (S_OK != hr)
{
// Failure while inviting:
m_cnResult = CN_RC_INVITE_FAILED;
}
}
}
if (CN_RC_NOERROR != m_cnResult)
{
SetCallState(CNS_COMPLETE);
}
}
else if (fRemoteInConf || fMCU)
{
TRACE_OUT(("COutgoingCall - QR succeeded (>0 conf) -> joining"));
TRACE_OUT(("\tfMCU is %d", fMCU));
// There are remote conferences
HRESULT hr = E_FAIL; // Assume a failure
SetCallState(CNS_JOINING_REMOTE);
if (!fMCU && (NULL == pwszConfNames[1]))
{
// we're not calling an MCU and we have just one conference, so join it
m_bstrConfToJoin = SysAllocString(pwszConfNames[0]);
hr = m_pConfObject->JoinConference( m_bstrConfToJoin,
m_bstrPassword,
m_pszAddr,
m_bstrUserString);
}
else
{
ASSERT(NULL == m_bstrConfToJoin);
REMOTE_CONFERENCE remoteConf;
remoteConf.fMCU = fMCU;
remoteConf.pwszConfNames = pwszConfNames;
remoteConf.pbstrConfToJoin = &m_bstrConfToJoin;
// Ask the app which conference to join
NotifySink(&remoteConf, OnNotifyRemoteConference);
if (NULL != m_bstrConfToJoin)
{
hr = m_pConfObject->JoinConference( m_bstrConfToJoin,
m_bstrPassword,
m_pszAddr,
m_bstrUserString);
}
}
if (S_OK != hr)
{
// JoinConference failed!
m_cnResult = CN_RC_JOIN_FAILED;
SetCallState(CNS_COMPLETE);
}
}
else
{
if (CNS_QUERYING_REMOTE_AFTER_INVITE == m_cnState)
{
m_cnResult = CN_RC_CONFERENCE_INVITE_DENIED;
SetCallState(CNS_COMPLETE);
}
else
{
// No conferences on remote machine, so create local:
TRACE_OUT(("COutgoingCall - QR succeeded (no conf)-> creating local"));
// Create local conf
ASSERT(m_pConfObject);
SetCallState(CNS_CREATING_LOCAL);
HRESULT hr = m_pConfObject->CreateConference();
if (S_OK != hr)
{
// CreateConference failed!
m_cnResult = CN_RC_CONFERENCE_CREATE_FAILED;
SetCallState(CNS_COMPLETE);
}
}
}
}
else
{
// The QueryRemote failed
switch( ncsResult )
{
case UI_RC_USER_REJECTED:
// The initial QueryRemote failed because GCC symmetry determined
// that the other node is calling someone, and it might be us
// See Bug 1886
TRACE_OUT(("COutgoingCall - QueryRemote rejected -> complete"));
m_cnResult = CN_RC_REMOTE_PLACING_CALL;
break;
case UI_RC_T120_REMOTE_REQUIRE_SECURITY:
m_cnResult = CN_RC_CONNECT_REMOTE_REQUIRE_SECURITY;
break;
case UI_RC_T120_SECURITY_FAILED:
m_cnResult = CN_RC_SECURITY_FAILED;
break;
case UI_RC_T120_REMOTE_NO_SECURITY:
m_cnResult = CN_RC_CONNECT_REMOTE_NO_SECURITY;
break;
case UI_RC_T120_REMOTE_DOWNLEVEL_SECURITY:
m_cnResult = CN_RC_CONNECT_REMOTE_DOWNLEVEL_SECURITY;
break;
case UI_RC_T120_AUTHENTICATION_FAILED:
m_cnResult = CN_RC_CONNECT_AUTHENTICATION_FAILED;
break;
default:
m_cnResult = CN_RC_CONNECT_FAILED;
break;
}
SetCallState(CNS_COMPLETE);
}
DebugExitBOOL(COutgoingCall::OnQueryRemoteResult, TRUE);
return TRUE;
}
BOOL COutgoingCall::OnConferenceStarted(CONF_HANDLE hNewConf, HRESULT ncsResult)
{
DebugEntry(COutgoingCall::OnConferenceStarted);
switch (m_cnState)
{
case CNS_CREATING_LOCAL:
{
TRACE_OUT(("COutgoingCall (inviting) received ConferenceStarted event"));
if (0 == ncsResult)
{
ASSERT(m_pConfObject);
ASSERT(NULL == m_hRequest);
SetCallState(CNS_INVITING_REMOTE);
HRESULT hr = m_pConfObject->InviteConference(m_pszAddr, m_bstrUserString, &m_hRequest);
if (S_OK != hr)
{
m_hRequest = NULL;
m_cnResult = CN_RC_INVITE_FAILED;
SetCallState(CNS_COMPLETE);
// Issue "soft" leave attempt (to allow auto-terminate)
ASSERT(m_pConfObject);
HRESULT hr = m_pConfObject->LeaveConference(FALSE);
if (FAILED(hr))
{
WARNING_OUT(("Couldn't leave after failed invite"));
}
}
}
else
{
WARNING_OUT(("CreateConference (local) failed - need UI here!"));
m_cnResult = CN_RC_CONFERENCE_CREATE_FAILED;
SetCallState(CNS_COMPLETE);
}
break;
}
case CNS_JOINING_REMOTE:
{
TRACE_OUT(("COutgoingCall (joining) received ConferenceStarted event"));
if (0 == ncsResult)
{
SetCallState(CNS_COMPLETE);
if (m_pH323Connection)
{
UINT uNodeID = hNewConf->GetParentNodeID();
m_pConfObject->OnT120Connected(m_pH323Connection, uNodeID);
}
}
else if (UI_RC_INVALID_PASSWORD == ncsResult)
{
TRACE_OUT(("COutgoingCall - invalid password, prompt for password"));
BSTR bstrPassword = NULL;
REMOTE_PASSWORD remotePw;
remotePw.bstrConference = m_bstrConfToJoin;
remotePw.pbstrPassword = &bstrPassword;
if (NO_ERROR != hNewConf->GetCred(&remotePw.pbRemoteCred, &remotePw.cbRemoteCred))
{
remotePw.pbRemoteCred = NULL;
remotePw.cbRemoteCred = 0;
}
remotePw.fIsService = m_fService;
NotifySink(&remotePw, OnNotifyRemotePassword);
if (NULL != bstrPassword)
{
SysFreeString(m_bstrPassword);
m_bstrPassword = bstrPassword;
// reissue join with new password
ASSERT(m_pConfObject);
HRESULT ncs =
m_pConfObject->JoinConference( m_bstrConfToJoin,
m_bstrPassword,
m_pszAddr,
m_bstrUserString,
TRUE); // retry
if (0 != ncs)
{
// JoinConference failed!
m_cnResult = CN_RC_JOIN_FAILED;
SetCallState(CNS_COMPLETE);
}
}
else
{
// cancel from pw dlg
m_cnResult = CN_RC_INVALID_PASSWORD;
SetCallState(CNS_COMPLETE);
ASSERT(m_pConfObject);
HRESULT hr = m_pConfObject->LeaveConference(TRUE);
if (FAILED(hr))
{
ERROR_OUT(("Couldn't leave after cancelling pw join!"));
}
}
}
else if (UI_RC_UNKNOWN_CONFERENCE == ncsResult)
{
TRACE_OUT(("Join failed (conf does not exist) "
"- notifying user"));
// error while joining
m_cnResult = CN_RC_CONFERENCE_DOES_NOT_EXIST;
SetCallState(CNS_COMPLETE);
}
else
{
TRACE_OUT(("Join failed - notifying user"));
// error while joining
m_cnResult = CN_RC_CONFERENCE_JOIN_DENIED;
SetCallState(CNS_COMPLETE);
}
break;
}
default:
{
if (m_pConfObject->GetConfHandle() == hNewConf)
{
WARNING_OUT(("COutgoingCall received unexpected ConferenceStarted event"));
}
else
{
TRACE_OUT(("COutgoingCall ignoring ConferenceStarted event - not our conf"));
}
}
}
DebugExitBOOL(COutgoingCall::OnConferenceStarted, TRUE);
return TRUE;
}
BOOL COutgoingCall::OnH323Connected(IH323Endpoint * pConnection)
{
DebugEntry(COutgoingCall::OnH323Connected);
BOOL bRet = TRUE;
ASSERT(m_pH323Connection == pConnection);
if (m_dwFlags & CRPCF_DATA)
{
PCC_VENDORINFO pLocalVendorInfo;
PCC_VENDORINFO pRemoteVendorInfo;
pConnection->GetVersionInfo(&pLocalVendorInfo, &pRemoteVendorInfo);
H323VERSION version = GetH323Version(pRemoteVendorInfo);
if (H323_NetMeeting20 == version)
{
if ((m_addrType != NM_ADDR_H323_GATEWAY) &&
(m_addrType != NM_ADDR_PSTN) &&
(m_addrType != NM_ADDR_ALIAS_ID) &&
(m_addrType != NM_ADDR_ALIAS_E164))
{
CNSTATUS cnResult = StartT120Call();
if (CN_RC_NOERROR != cnResult)
{
m_cnResult = cnResult;
SetCallState(CNS_COMPLETE);
}
}
else
{
SetCallState(CNS_COMPLETE);
}
}
else
{
// wait for results of T.120 open logical channel
TRACE_OUT(("COutgoingCall - H323Connected received -> waiting for T120 channel open"));
SetCallState(CNS_WAITING_T120_OPEN);
}
}
else
{
// no T120 for this call
TRACE_OUT(("COutgoingCall - our H323 call started -> complete"));
SetCallState(CNS_COMPLETE);
}
CConfObject *pco = ::GetConfObject();
if (NULL != pco)
{
// add member if we are calling with A/V
BOOL fAddMember = m_dwFlags & (CRPCF_AUDIO | CRPCF_VIDEO);
pco->OnH323Connected(pConnection, m_dwFlags, fAddMember, GUID_NULL);
}
DebugExitBOOL(COutgoingCall::OnH323Connected, bRet);
return bRet;
}
BOOL COutgoingCall::OnH323Disconnected(IH323Endpoint * pConnection)
{
DebugEntry(COutgoingCall::OnH323Disconnected);
BOOL bRet = TRUE;
ASSERT(m_pH323Connection == pConnection);
HRESULT hSummary = m_pH323Connection->GetSummaryCode();
// Check summary code for gatekeeper-caused disconnect
// Note: this applies only to MY gatekeeper, not the callee's gatekeeper
if(CUSTOM_FACILITY(hSummary) == FACILITY_GKIADMISSION)
{
DWORD dwRejectReason;
dwRejectReason = CUSTOM_FACILITY_CODE(hSummary);
// should the T.120 call be placed anyway?
switch (dwRejectReason)
{
case ARJ_CALLEE_NOT_REGISTERED:
m_cnResult = CN_RC_GK_CALLEE_NOT_REGISTERED;
break;
case ARJ_TIMEOUT:
m_cnResult = CN_RC_GK_TIMEOUT;
break;
case ARJ_INVALID_PERMISSION:
case ARJ_REQUEST_DENIED:
case ARJ_UNDEFINED:
case ARJ_CALLER_NOT_REGISTERED:
case ARJ_ROUTE_TO_GK:
case ARJ_INVALID_ENDPOINT_ID:
case ARJ_RESOURCE_UNAVAILABLE:
case ARJ_SECURTY_DENIAL:
case ARJ_QOS_CONTROL_NOT_SUPPORTED:
case ARJ_INCOMPLETE_ADDRESS:
default:
m_cnResult = CN_RC_GK_REJECTED;
break;
}
SetCallState(CNS_COMPLETE);
}
else if (CNS_CONNECTING_H323 == m_cnState)
{
// attempted an H.323 call without success.
TRACE_OUT(("COutgoingCall - our leading H323 call ended -> complete"));
// check for cases of remote refusing the call
switch (hSummary)
{
case CCR_REMOTE_DISCONNECT:
case CCR_REMOTE_REJECTED:
m_cnResult = CN_RC_CONFERENCE_INVITE_DENIED;
SetCallState(CNS_COMPLETE);
break;
case CCR_LOCAL_DISCONNECT:
// cancelled by user, do not report error
m_cnResult = CN_RC_LOCAL_CANCELED;
m_fCanceled = TRUE;
SetCallState(CNS_COMPLETE);
break;
case CCR_GK_NO_RESOURCES:
// callee's gatekeeper would not let callee answer
// do the same as CCR_REMOTE_REJECTED
// as a temporary measure !!!!
m_cnResult = CN_RC_CONFERENCE_INVITE_DENIED;
SetCallState(CNS_COMPLETE);
break;
case CCR_REMOTE_SECURITY_DENIED:
if (m_pConfObject->IsConfObjSecure())
{
m_cnResult = CN_RC_CONNECT_REMOTE_NO_SECURITY;
}
else
{
m_cnResult = CN_RC_CONNECT_REMOTE_REQUIRE_SECURITY;
}
SetCallState(CNS_COMPLETE);
break;
case CCR_REMOTE_BUSY:
if (m_pConfObject->IsConferenceActive())
{
m_cnResult = CN_RC_INVITE_DENIED_REMOTE_IN_CONF;
SetCallState(CNS_COMPLETE);
break;
}
// else fall through
default:
if ((m_dwFlags & CRPCF_DATA) &&
(m_addrType != NM_ADDR_H323_GATEWAY) &&
(m_addrType != NM_ADDR_PSTN) &&
(m_addrType != NM_ADDR_ALIAS_ID) &&
(m_addrType != NM_ADDR_ALIAS_E164))
{
// The endpoint is not there, not listening for H.323 calls, or is busy,
// and the address is a resolvable type (not an H.323 alias or E.164 number)
// go back into the T.120 call state path
CNSTATUS cnResult = StartT120Call();
if (CN_RC_NOERROR != cnResult)
{
m_cnResult = cnResult;
SetCallState(CNS_COMPLETE);
bRet = FALSE;
}
}
else
{
switch (hSummary)
{
case CCR_REMOTE_BUSY:
m_cnResult = CN_RC_INVITE_DENIED_REMOTE_IN_CONF;
break;
case CCR_REMOTE_MEDIA_ERROR:
m_cnResult = CN_RC_AUDIO_IN_USE_REMOTE;
break;
case CCR_LOCAL_MEDIA_ERROR:
m_cnResult = CN_RC_AUDIO_IN_USE_LOCAL;
break;
case CCR_GK_NO_RESOURCES:
m_cnResult = CN_RC_GK_REJECTED;
break;
default:
m_cnResult = CN_RC_AUDIO_CONNECT_FAILED;
break;
}
SetCallState(CNS_COMPLETE);
}
break;
}
}
else if (CNS_WAITING_T120_OPEN == m_cnState)
{
TRACE_OUT(("COutgoingCall - OnH323Disconnected received -> call is canceled"));
m_cnResult = CN_RC_CONFERENCE_INVITE_DENIED;
SetCallState(CNS_COMPLETE);
}
else
{
TRACE_OUT(("COutgoingCall - OnH323Disconnected received -> canceling call"));
ASSERT(m_pConfObject);
if (NULL != m_hRequest)
{
REQUEST_HANDLE hRequest = m_hRequest;
m_hRequest = NULL;
m_pConfObject->CancelInvite(hRequest);
}
if (m_pConfObject->IsConferenceActive())
{
// Issue "soft" leave attempt (to allow auto-terminate)
m_pConfObject->LeaveConference(FALSE);
}
}
// the connection may have been released due to the LeaveConference above
// bug 3996
if (m_pH323Connection)
{
m_pH323Connection->Release();
m_pH323Connection = NULL;
}
DebugExitBOOL(COutgoingCall::OnH323Disconnected, bRet);
return bRet;
}
BOOL COutgoingCall::OnT120ChannelOpen(ICommChannel *pIChannel, DWORD dwStatus)
{
DebugEntry(COutgoingCall::OnT120ChannelOpen);
BOOL bRet = TRUE;
// sometimes we get an extra T120 channel open
// this is most likely due the master/slave anomolies that
// venkatg discovered.
if (CNS_WAITING_T120_OPEN == m_cnState)
{
switch(dwStatus)
{
case CHANNEL_OPEN:
{
// start the T.120 part of the call
// get the negotiated address
SOCKADDR_IN sin_T120;
HRESULT hr = pIChannel->GetRemoteAddress(&sin_T120);
delete m_pszAddr; // we won't be needing this anymore
TCHAR szAddress[256]; // allow plenty of room for conversion from numberic to string
wsprintf(szAddress, "%s:%d", inet_ntoa(sin_T120.sin_addr), ntohs(sin_T120.sin_port));
m_pszAddr = PszAlloc(szAddress);
CNSTATUS cnResult = StartT120Call();
if (CN_RC_NOERROR != cnResult)
{
m_cnResult = cnResult;
SetCallState(CNS_COMPLETE);
}
}
break;
case CHANNEL_CLOSED:
// if the T.120 channel is being closed
// the H.323 call is going down, so don't continue waiting for
// T.120 to connect!
m_cnResult = CN_RC_CONNECT_FAILED;
SetCallState(CNS_COMPLETE);
if (NULL != m_hRequest)
{
REQUEST_HANDLE hRequest = m_hRequest;
m_hRequest = NULL;
ASSERT(m_pConfObject);
m_pConfObject->CancelInvite(hRequest);
}
break;
case CHANNEL_NO_CAPABILITY:
case CHANNEL_REJECTED:
case CHANNEL_OPEN_ERROR:
default:
m_cnResult = CN_RC_CONNECT_FAILED;
SetCallState(CNS_COMPLETE);
break;
}
}
DebugExitBOOL(COutgoingCall::OnT120ChannelOpen, bRet);
return bRet;
}
void COutgoingCall::CallComplete()
{
DebugEntry(COutgoingCall::CallComplete);
// If this fails, we are being destructed unexpectedly
ASSERT( (m_cnState == CNS_IDLE) ||
(m_cnState == CNS_COMPLETE));
// The request handle should have been reset
ASSERT(NULL == m_hRequest);
// make sure that the H323 connection is released
if (m_pH323Connection)
{
if (!IsDataOnly() &&
((CN_RC_TRANSPORT_FAILURE == m_cnResult) ||
(CN_RC_QUERY_FAILED == m_cnResult) ||
(CN_RC_CONNECT_FAILED == m_cnResult)))
{
m_cnResult = CN_RC_NOERROR;
}
if (CN_RC_NOERROR != m_cnResult)
{
m_pH323Connection->Disconnect();
}
m_pH323Connection->Release();
m_pH323Connection = NULL;
}
if (!FCanceled() && (CN_RC_NOERROR != m_cnResult))
{
ReportError(m_cnResult);
}
NM_CALL_STATE state;
GetState(&state);
NotifySink((PVOID) state, OnNotifyCallStateChanged);
TRACE_OUT(("ConfNode destroying addr %s", m_pszAddr));
DebugExitVOID(COutgoingCall::CallComplete);
}
BOOL COutgoingCall::ReportError(CNSTATUS cns)
{
DebugEntry(COutgoingCall::ReportError);
TRACE_OUT(("CNSTATUS 0x%08x", cns));
NotifySink((PVOID)cns, OnNotifyCallError);
DebugExitBOOL(COutgoingCall::ReportError, TRUE);
return TRUE;
}
CNSTATUS COutgoingCall::StartT120Call()
{
DebugEntry(COutgoingCall::StartT120Call);
CNSTATUS cnResult = CN_RC_NOERROR;
if (NULL == m_bstrConfToJoin)
{
// conference name not specified
// need to start out with a QueryRemote
SetCallState(CNS_QUERYING_REMOTE);
ASSERT(g_pNodeController);
HRESULT hr = g_pNodeController->QueryRemote(this, m_pszAddr,
m_pConfObject->IsConfObjSecure(),
m_pConfObject->IsConferenceActive());
if (S_OK != hr)
{
cnResult = CN_RC_QUERY_FAILED;
}
}
else
{
ASSERT(m_pConfObject);
// conference name has been specified
// time to do a JoinConference
SetCallState(CNS_JOINING_REMOTE);
HRESULT hr = m_pConfObject->JoinConference( m_bstrConfToJoin,
m_bstrPassword,
m_pszAddr,
m_bstrUserString);
if (S_OK != hr)
{
cnResult = CN_RC_JOIN_FAILED;
}
}
DebugExitINT(COutgoingCall::StartT120Call, cnResult);
return cnResult;
}
VOID COutgoingCall::SetCallState(CNODESTATE cnState)
{
NM_CALL_STATE stateOld;
NM_CALL_STATE stateNew;
GetState(&stateOld);
m_cnState = cnState;
// completion state will be fired off later
if (CNS_COMPLETE != cnState)
{
GetState(&stateNew);
if (stateOld != stateNew)
{
NotifySink((PVOID) stateNew, OnNotifyCallStateChanged);
}
}
}
HRESULT COutgoingCall::_Cancel(BOOL fLeaving)
{
DebugEntry(COutgoingCall::Cancel);
BOOL fAbortT120 = (m_cnState != CNS_COMPLETE);
if (fAbortT120)
{
m_fCanceled = TRUE;
// Abort T.120 Call:
// Attempt to make this transition regardless of our
// current state:
SetCallState(CNS_COMPLETE);
ASSERT(m_pConfObject);
if (NULL != m_hRequest)
{
REQUEST_HANDLE hRequest = m_hRequest;
m_hRequest = NULL;
m_pConfObject->CancelInvite(hRequest);
}
if (!fLeaving && m_pConfObject->IsConferenceActive())
{
HRESULT hr = m_pConfObject->LeaveConference(FALSE);
if (FAILED(hr))
{
WARNING_OUT(("Couldn't leave after disconnecting"));
}
}
}
if (NULL != m_pH323Connection)
{
m_fCanceled = TRUE;
// Abort H.323 Call:
m_pH323Connection->Disconnect();
}
DebugExitULONG(COutgoingCall::Abort, m_cnResult);
return CN_RC_NOERROR ? S_OK : E_FAIL;
}
STDMETHODIMP_(ULONG) COutgoingCall::AddRef(void)
{
return RefCount::AddRef();
}
STDMETHODIMP_(ULONG) COutgoingCall::Release(void)
{
return RefCount::Release();
}
HRESULT STDMETHODCALLTYPE COutgoingCall::QueryInterface(REFIID riid, PVOID *ppv)
{
HRESULT hr = S_OK;
if ((riid == IID_INmCall) || (riid == IID_IUnknown))
{
*ppv = (INmCall *)this;
ApiDebugMsg(("COutgoingCall::QueryInterface()"));
}
else if (riid == IID_IConnectionPointContainer)
{
*ppv = (IConnectionPointContainer *) this;
ApiDebugMsg(("CNmCall::QueryInterface(): Returning IConnectionPointContainer."));
}
else
{
hr = E_NOINTERFACE;
*ppv = NULL;
ApiDebugMsg(("COutgoingCall::QueryInterface(): Called on unknown interface."));
}
if (S_OK == hr)
{
AddRef();
}
return hr;
}
HRESULT COutgoingCall::IsIncoming(void)
{
return S_FALSE;
}
HRESULT COutgoingCall::GetState(NM_CALL_STATE *pState)
{
HRESULT hr = E_POINTER;
if (NULL != pState)
{
if (FCanceled())
{
*pState = NM_CALL_CANCELED;
}
else
{
switch (m_cnState)
{
case CNS_IDLE:
*pState = NM_CALL_INIT;
break;
case CNS_SEARCHING:
*pState = NM_CALL_SEARCH;
break;
case CNS_CONNECTING_H323:
case CNS_WAITING_T120_OPEN:
case CNS_QUERYING_REMOTE:
case CNS_CREATING_LOCAL:
case CNS_INVITING_REMOTE:
case CNS_JOINING_REMOTE:
*pState = NM_CALL_WAIT;
break;
case CNS_COMPLETE:
switch (m_cnResult)
{
case CN_RC_NOERROR:
*pState = NM_CALL_ACCEPTED;
break;
case CN_RC_CONFERENCE_JOIN_DENIED:
case CN_RC_CONFERENCE_INVITE_DENIED:
case CN_RC_CONFERENCE_DOES_NOT_EXIST:
case CN_RC_AUDIO_CONNECT_FAILED:
case CN_RC_GK_CALLEE_NOT_REGISTERED:
case CN_RC_GK_TIMEOUT:
case CN_RC_GK_REJECTED:
case CN_RC_GK_NOT_REGISTERED:
case CN_RC_CONNECT_REMOTE_NO_SECURITY:
case CN_RC_CONNECT_REMOTE_DOWNLEVEL_SECURITY:
case CN_RC_CONNECT_REMOTE_REQUIRE_SECURITY:
case CN_RC_TRANSPORT_FAILURE:
case CN_RC_QUERY_FAILED:
case CN_RC_CONNECT_FAILED:
*pState = NM_CALL_REJECTED;
break;
case CN_RC_ALREADY_IN_CONFERENCE:
case CN_RC_CANT_INVITE_MCU:
case CN_RC_CANT_JOIN_ALREADY_IN_CALL:
case CN_RC_INVITE_DENIED_REMOTE_IN_CONF:
case CN_RC_REMOTE_PLACING_CALL:
case CN_RC_ALREADY_IN_CONFERENCE_MCU:
case CN_RC_INVALID_PASSWORD:
default:
*pState = NM_CALL_CANCELED;
break;
}
break;
default:
*pState = NM_CALL_INVALID;
break;
}
}
hr = S_OK;
}
return hr;
}
HRESULT COutgoingCall::GetName(BSTR * pbstrName)
{
if (NULL == pbstrName)
return E_POINTER;
*pbstrName = SysAllocString(m_bstrName);
return (*pbstrName ? S_OK : E_FAIL);
}
HRESULT COutgoingCall::GetAddr(BSTR *pbstrAddr, NM_ADDR_TYPE *puType)
{
if ((NULL == pbstrAddr) || (NULL == puType))
return E_POINTER;
*pbstrAddr = SysAllocString(CUSTRING(m_pszAddr));
*puType = m_addrType;
return *pbstrAddr ? S_OK : E_FAIL;
}
HRESULT COutgoingCall::GetUserData(REFGUID rguid, BYTE **ppb, ULONG *pcb)
{
return E_NOTIMPL;
}
HRESULT COutgoingCall::GetConference(INmConference **ppConference)
{
HRESULT hr = E_POINTER;
if (NULL != ppConference)
{
*ppConference = m_pConfObject;
return S_OK;
}
return hr;
}
HRESULT COutgoingCall::Accept(void)
{
return E_UNEXPECTED;
}
HRESULT COutgoingCall::Reject(void)
{
return E_UNEXPECTED;
}
HRESULT COutgoingCall::Cancel(void)
{
DebugEntry(COutgoingCall::Cancel);
AddRef(); // protect against Release() while processing
// disconnect related indications & callbacks
HRESULT hr = _Cancel(FALSE);
if (FIsComplete())
{
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
ASSERT(NULL !=pOprahNCUI);
pOprahNCUI->OnOutgoingCallCanceled(this);
}
DebugExitULONG(COutgoingCall::Abort, m_cnResult);
Release();
return hr;
}
/* O N N O T I F Y C A L L E R R O R */
/*-------------------------------------------------------------------------
%%Function: OnNotifyCallError
-------------------------------------------------------------------------*/
HRESULT OnNotifyCallError(IUnknown *pCallNotify, PVOID pv, REFIID riid)
{
ASSERT(NULL != pCallNotify);
CNSTATUS cnStatus = (CNSTATUS)((DWORD_PTR)pv);
switch (cnStatus)
{
case CN_RC_ALREADY_IN_CONFERENCE:
case CN_RC_CANT_INVITE_MCU:
case CN_RC_CANT_JOIN_ALREADY_IN_CALL:
case CN_RC_INVITE_DENIED_REMOTE_IN_CONF:
case CN_RC_REMOTE_PLACING_CALL:
case CN_RC_ALREADY_IN_CONFERENCE_MCU:
((INmCallNotify*)pCallNotify)->NmUI(CONFN_CALL_IN_CONFERENCE);
break;
case CN_RC_CONFERENCE_JOIN_DENIED:
case CN_RC_CONFERENCE_INVITE_DENIED:
case CN_RC_CONFERENCE_DOES_NOT_EXIST:
case CN_RC_GK_CALLEE_NOT_REGISTERED:
case CN_RC_GK_TIMEOUT:
case CN_RC_GK_REJECTED:
case CN_RC_GK_NOT_REGISTERED:
case CN_RC_CONNECT_REMOTE_NO_SECURITY:
case CN_RC_CONNECT_REMOTE_DOWNLEVEL_SECURITY:
case CN_RC_CONNECT_REMOTE_REQUIRE_SECURITY:
((INmCallNotify*)pCallNotify)->NmUI(CONFN_CALL_IGNORED);
break;
case CN_RC_CONNECT_FAILED:
case CN_RC_AUDIO_CONNECT_FAILED:
((INmCallNotify*)pCallNotify)->NmUI(CONFN_CALL_FAILED);
break;
default:
break;
}
if (IID_INmCallNotify2 == riid)
{
((INmCallNotify2*)pCallNotify)->CallError(cnStatus);
}
return S_OK;
}
/* O N N O T I F Y R E M O T E C O N F E R E N C E */
/*-------------------------------------------------------------------------
%%Function: OnNotifyRemoteConference
-------------------------------------------------------------------------*/
HRESULT OnNotifyRemoteConference(IUnknown *pCallNotify, PVOID pv, REFIID riid)
{
REMOTE_CONFERENCE *prc = (REMOTE_CONFERENCE *)pv;
// WARNING: pwszConfName is an PWSTR array, not a BSTR
ASSERT(NULL != pCallNotify);
((INmCallNotify2*)pCallNotify)->RemoteConference(prc->fMCU,
(BSTR *) prc->pwszConfNames, prc->pbstrConfToJoin);
return S_OK;
}
/* O N N O T I F Y R E M O T E P A S S W O R D */
/*-------------------------------------------------------------------------
%%Function: OnNotifyRemotePassword
-------------------------------------------------------------------------*/
HRESULT OnNotifyRemotePassword(IUnknown *pCallNotify, PVOID pv, REFIID riid)
{
REMOTE_PASSWORD *prp = (REMOTE_PASSWORD *)pv;
ASSERT(NULL != pCallNotify);
((INmCallNotify2*)pCallNotify)->RemotePassword(prp->bstrConference, prp->pbstrPassword, prp->pbRemoteCred, prp->cbRemoteCred, prp->fIsService);
return S_OK;
}
COutgoingCallManager::COutgoingCallManager()
{
}
COutgoingCallManager::~COutgoingCallManager()
{
// Empty the call list:
while (!m_CallList.IsEmpty())
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.RemoveHead();
// Shouldn't have any NULL entries:
ASSERT(pCall);
pCall->Release();
}
}
UINT COutgoingCallManager::GetCallCount()
{
UINT nNodes = 0;
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
nNodes++;
m_CallList.GetNext(pos);
}
return nNodes;
}
COutgoingCall* COutgoingCallManager::FindCall(IH323Endpoint * lpConnection)
{
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
if ((NULL != pCall) &&
(pCall->GetH323Connection() == lpConnection))
{
return pCall;
}
}
return NULL;
}
BOOL COutgoingCallManager::MatchActiveCallee(LPCTSTR pszDest, BSTR bstrAlias, BSTR bstrConference)
{
// Try to find a matching callee
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
if (NULL != pCall)
{
if (pCall->MatchActiveCallee(pszDest, bstrAlias, bstrConference))
{
return TRUE;
}
}
}
return FALSE;
}
HRESULT COutgoingCallManager::Call(
INmCall **ppCall,
COprahNCUI* pManager,
DWORD dwFlags,
NM_ADDR_TYPE addrType,
BSTR bstrName,
BSTR bstrSetup,
BSTR bstrDest,
BSTR bstrAlias,
BSTR bstrURL,
BSTR bstrConference,
BSTR bstrPassword,
BSTR bstrUserString)
{
DebugEntry(COutgoingCallManager::CallConference);
HRESULT hr = E_FAIL;
COutgoingCall* pCall = NULL;
CConfObject* pConfObject = pManager->GetConfObject();
if (NULL != ppCall)
{
*ppCall = NULL;
}
if (MatchActiveCallee(CUSTRING(bstrDest), bstrAlias, bstrConference))
{
hr = NM_CALLERR_ALREADY_CALLING;
}
else if (pConfObject->IsConferenceActive() && (NULL != bstrConference))
{
hr= NM_CALLERR_IN_CONFERENCE;
}
else
{
if (!pConfObject->IsConferenceActive())
{
pConfObject->SetConfSecurity(0 != (CRPCF_SECURE & dwFlags));
}
//
// Check outgoing call settings
//
if (pConfObject->GetNumMembers() >= pConfObject->GetConfMaxParticipants())
{
ASSERT(pConfObject->GetNumMembers() == pConfObject->GetConfMaxParticipants());
WARNING_OUT(("Outgoing call denied, reached limit of participants"));
goto END_CALL;
}
if ((pConfObject->IsHosting() != S_OK) &&
!(pConfObject->GetConfAttendeePermissions() & NM_PERMIT_OUTGOINGCALLS))
{
WARNING_OUT(("Outgoing call denied, not permitted by meeting settings"));
goto END_CALL;
}
pCall = new COutgoingCall( pConfObject,
dwFlags,
addrType,
bstrName,
bstrDest,
bstrAlias,
bstrConference,
bstrPassword,
bstrUserString);
if (NULL != pCall)
{
m_CallList.AddTail(pCall);
if (NULL != ppCall)
{
pCall->AddRef();
// This MUST be set before OnNotifyCallCreated
*ppCall = pCall;
}
pCall->AddRef();
pManager->OnOutgoingCallCreated(pCall);
pCall->PlaceCall();
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
// let the caller know that we successfully created the call
// any error will be reported asynchronously
hr = S_OK;
}
}
END_CALL:
DebugExitHRESULT(COutgoingCallManager::CallConference, hr);
return hr;
}
BOOL COutgoingCallManager::RemoveFromList(COutgoingCall* pCall)
{
DebugEntry(COutgoingCallManager::RemoveFromList);
ASSERT(pCall);
BOOL bRet = FALSE;
POSITION pos = m_CallList.GetPosition(pCall);
if (NULL != pos)
{
m_CallList.RemoveAt(pos);
pCall->CallComplete();
pCall->Release();
bRet = TRUE;
}
else
{
WARNING_OUT(("COutgoingCallManager::RemoveFromList() could not match call"));
}
DebugExitBOOL(COutgoingCallManager::RemoveFromList, bRet);
return bRet;
}
BOOL COutgoingCallManager::OnH323Connected(IH323Endpoint* lpConnection)
{
DebugEntry(COutgoingCallManager::OnH323Connected);
BOOL fFound = FALSE;
COutgoingCall* pCall = FindCall(lpConnection);
if (pCall)
{
fFound = TRUE;
pCall->AddRef();
pCall->OnH323Connected(lpConnection);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
}
DebugExitBOOL(COutgoingCallManager::OnH323Connected, fFound);
return fFound;
}
BOOL COutgoingCallManager::OnH323Disconnected(IH323Endpoint * lpConnection)
{
DebugEntry(COutgoingCallManager::OnH323Disconnected);
BOOL fFound = FALSE;
COutgoingCall* pCall = FindCall(lpConnection);
if (pCall)
{
fFound = TRUE;
pCall->AddRef();
pCall->OnH323Disconnected(lpConnection);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
}
DebugExitBOOL(COutgoingCallManager::OnH323Disconnected, fFound);
return fFound;
}
VOID COutgoingCallManager::OnT120ChannelOpen(ICommChannel *pIChannel, IH323Endpoint * lpConnection, DWORD dwStatus)
{
DebugEntry(COutgoingCallManager::OnT120ChannelOpen);
COutgoingCall* pCall = FindCall(lpConnection);
if (pCall)
{
pCall->AddRef();
pCall->OnT120ChannelOpen(pIChannel, dwStatus);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
}
DebugExitVOID(COutgoingCallManager::OnT120ChannelOpen);
}
VOID COutgoingCallManager::OnConferenceStarted(CONF_HANDLE hConference, HRESULT hResult)
{
DebugEntry(COutgoingCallManager::OnConferenceStarted);
// Tell all ConfNode's that a conference has started
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
if (NULL != pCall)
{
pCall->AddRef();
pCall->OnConferenceStarted(hConference, hResult);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
}
}
DebugExitVOID(COutgoingCallManager::OnConferenceStarted);
}
VOID COutgoingCallManager::OnQueryRemoteResult(PVOID pvCallerContext,
HRESULT hResult,
BOOL fMCU,
PWSTR* ppwszConferenceNames,
PT120PRODUCTVERSION pVersion,
PWSTR* ppwszConfDescriptors)
{
DebugEntry(COutgoingCallManager::OnQueryRemoteResult);
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
// Notify the node that issued the query:
if ((COutgoingCall*) pvCallerContext == pCall)
{
pCall->AddRef();
pCall->OnQueryRemoteResult( hResult,
fMCU,
ppwszConferenceNames,
pVersion,
ppwszConfDescriptors);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
break;
}
}
DebugExitVOID(COutgoingCallManager::OnQueryRemoteResult);
}
VOID COutgoingCallManager::OnInviteResult( CONF_HANDLE hConference,
REQUEST_HANDLE hRequest,
UINT uNodeID,
HRESULT hResult,
PT120PRODUCTVERSION pVersion)
{
DebugEntry(COutgoingCallManager::OnInviteResult);
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
if ((NULL != pCall) &&
(pCall->GetCurrentRequestHandle() == hRequest))
{
pCall->AddRef();
pCall->OnInviteResult(hResult, uNodeID);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
break;
}
}
DebugExitVOID(COutgoingCallManager::OnInviteResult);
}
VOID COutgoingCallManager::OnConferenceEnded(CONF_HANDLE hConference)
{
DebugEntry(COutgoingCallManager::OnConferenceEnded);
// Tell all ConfNode's that a conference has started
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
if (NULL != pCall)
{
pCall->AddRef();
pCall->OnConferenceEnded();
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
}
}
DebugExitVOID(COutgoingCallManager::OnConferenceEnded);
}
VOID COutgoingCallManager::CancelCalls()
{
DebugEntry(COutgoingCallManager::CancelCalls);
// Tell all ConfNode's that a conference has started
POSITION pos = m_CallList.GetHeadPosition();
while (pos)
{
COutgoingCall* pCall = (COutgoingCall*) m_CallList.GetNext(pos);
if (NULL != pCall)
{
pCall->AddRef();
pCall->_Cancel(TRUE);
if (pCall->FIsComplete())
{
RemoveFromList(pCall);
}
pCall->Release();
}
}
DebugExitVOID(COutgoingCallManager::CancelCalls);
}