mirror of https://github.com/tongzx/nt5src
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.
1212 lines
26 KiB
1212 lines
26 KiB
// File: icall.cpp
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "icall.h"
|
|
#include "imanager.h"
|
|
#include <service.h>
|
|
#include <confguid.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}
|
|
};
|
|
|
|
// 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 bstrAddr,
|
|
BSTR bstrConference,
|
|
BSTR bstrPassword
|
|
) :
|
|
CConnectionPointContainer(g_apiidCP, ARRAY_ELEMENTS(g_apiidCP)),
|
|
m_pConfObject (pco),
|
|
m_addrType (addrType),
|
|
m_dwFlags (dwFlags),
|
|
m_bstrAddr (SysAllocString(bstrAddr)),
|
|
m_bstrConfToJoin (SysAllocString(bstrConference)),
|
|
m_bstrPassword (SysAllocString(bstrPassword)),
|
|
m_hRequest (NULL),
|
|
m_fCanceled (FALSE),
|
|
m_cnResult (CN_RC_NOERROR),
|
|
m_cnState (CNS_IDLE),
|
|
m_fService (FALSE)
|
|
{
|
|
m_pszAddr = PszAlloc(CUSTRING(bstrAddr));
|
|
TRACE_OUT(("Obj: %08X created COutgoingCall", this));
|
|
}
|
|
|
|
COutgoingCall::~COutgoingCall()
|
|
{
|
|
if (m_pszAddr)
|
|
{
|
|
delete m_pszAddr;
|
|
m_pszAddr = NULL;
|
|
}
|
|
|
|
if (m_bstrAddr)
|
|
{
|
|
SysFreeString(m_bstrAddr);
|
|
m_bstrAddr = NULL;
|
|
}
|
|
|
|
TRACE_OUT(("Obj: %08X destroyed COutgoingCall", this));
|
|
}
|
|
|
|
|
|
/* 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 (NULL != g_pNodeController)
|
|
{
|
|
// Start placing the T.120 call
|
|
CNSTATUS cnResult = CN_RC_NOERROR;
|
|
|
|
if (NULL == m_bstrConfToJoin)
|
|
{
|
|
// conference name not specified
|
|
// need to start out with a QueryRemote
|
|
SetCallState(CNS_QUERYING_REMOTE);
|
|
|
|
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);
|
|
if (S_OK != hr)
|
|
{
|
|
cnResult = CN_RC_JOIN_FAILED;
|
|
}
|
|
}
|
|
|
|
if (CN_RC_NOERROR != cnResult)
|
|
{
|
|
m_cnResult = cnResult;
|
|
SetCallState(CNS_COMPLETE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_cnResult = CN_RC_TRANSPORT_FAILURE;
|
|
SetCallState(CNS_COMPLETE);
|
|
}
|
|
|
|
DebugExitVOID(COutgoingCall::PlaceCall);
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
}
|
|
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[],
|
|
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_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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
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_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);
|
|
}
|
|
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,
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
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"));
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
TRACE_OUT(("COutgoingCall::QueryInterface()"));
|
|
}
|
|
else if (riid == IID_IConnectionPointContainer)
|
|
{
|
|
*ppv = (IConnectionPointContainer *) this;
|
|
TRACE_OUT(("CNmCall::QueryInterface(): Returning IConnectionPointContainer."));
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
*ppv = NULL;
|
|
TRACE_OUT(("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_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_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::GetAddress(BSTR * pbstrAddr)
|
|
{
|
|
if (NULL == pbstrAddr)
|
|
return E_POINTER;
|
|
|
|
*pbstrAddr = SysAllocString(m_bstrAddr);
|
|
return (*pbstrAddr ? S_OK : E_FAIL);
|
|
}
|
|
|
|
|
|
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_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:
|
|
((INmCallNotify*)pCallNotify)->NmUI(CONFN_CALL_FAILED);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (IID_INmCallNotify == riid)
|
|
{
|
|
((INmCallNotify*)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);
|
|
((INmCallNotify*)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);
|
|
((INmCallNotify*)pCallNotify)->RemotePassword(prp->bstrConference, prp->pbstrPassword, prp->pbRemoteCred, prp->cbRemoteCred);
|
|
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;
|
|
}
|
|
|
|
|
|
HRESULT COutgoingCallManager::Call(
|
|
INmCall **ppCall,
|
|
COprahNCUI* pManager,
|
|
DWORD dwFlags,
|
|
NM_ADDR_TYPE addrType,
|
|
BSTR bstrAddr,
|
|
BSTR bstrConference,
|
|
BSTR bstrPassword)
|
|
{
|
|
DebugEntry(COutgoingCallManager::CallConference);
|
|
HRESULT hr = E_FAIL;
|
|
COutgoingCall* pCall = NULL;
|
|
CConfObject* pConfObject = pManager->GetConfObject();
|
|
|
|
if (NULL != ppCall)
|
|
{
|
|
*ppCall = NULL;
|
|
}
|
|
|
|
if (pConfObject->IsConferenceActive() && (NULL != bstrConference))
|
|
{
|
|
hr= NM_CALLERR_IN_CONFERENCE;
|
|
}
|
|
else
|
|
{
|
|
if (!pConfObject->IsConferenceActive())
|
|
{
|
|
pConfObject->SetConfSecurity(0 != (CRPCF_SECURE & dwFlags));
|
|
}
|
|
|
|
pCall = new COutgoingCall( pConfObject,
|
|
dwFlags,
|
|
addrType,
|
|
bstrAddr,
|
|
bstrConference,
|
|
bstrPassword);
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
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,
|
|
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)
|
|
{
|
|
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);
|
|
}
|