|
|
// File: call.cpp
#include "precomp.h"
#include "resource.h"
#include "call.h"
#include "dlgcall.h"
#include "confapi.h"
#include "popupmsg.h"
#include "passdlg.h"
#include "chcondlg.h"
#include "dshowdlg.h"
#include "conf.h"
#include "calllog.h"
#include "rostinfo.h"
#include "..\..\core\cncodes.h" // for CN_* codes
#include <inodecnt.h> // for UI_RC_...
#include "cr.h" // for CreateConfRoomWindow, UpdateUI
#include "confroom.h"
#include "confman.h"
#include "NmLdap.h"
#include "nmremote.h"
#include <tsecctrl.h>
#include "ConfPolicies.h"
#include "StatBar.h"
#include "certui.h"
#include "cmd.h"
#include "callto.h"
#include "dlgacd.h"
// External SDK stuff...
#include "NmCall.h"
#include "NmApp.h"
COBLIST * g_pCallList = NULL; // Global list of calls in progress
extern INmSysInfo2 * g_pNmSysInfo;
static HRESULT OnUIRemotePassword(BSTR bstrConference, BSTR *pbstrPassword, LPCTSTR pCertText, BOOL fIsService);
extern BOOL FRejectIncomingCalls(void); extern BOOL FIsConfRoomClosing(void); extern GUID g_csguidSecurity; extern GUID g_csguidMeetingSettings;
/* C C A L L */ /*-------------------------------------------------------------------------
%%Function: CCall -------------------------------------------------------------------------*/ CCall::CCall(LPCTSTR pszCallTo, LPCTSTR pszDisplayName, NM_ADDR_TYPE nmAddrType, BOOL bAddToMru, BOOL fIncoming) : RefCount(NULL), m_fIncoming (fIncoming), m_pszDisplayName (PszAlloc(pszDisplayName)), m_pszCallTo (PszAlloc(pszCallTo)), m_nmAddrType (nmAddrType), m_bAddToMru (bAddToMru), m_fSelectedConference (FALSE), m_pDlgCall (NULL), m_pInternalICall(NULL), m_pos (NULL), m_dwCookie (0), m_ppm (NULL), m_fInRespond (FALSE) { DbgMsg(iZONE_OBJECTS, "Obj: %08X created CCall", this);
if (FEmptySz(m_pszDisplayName)) { delete m_pszDisplayName;
// Default to "another person" if no name available in the call data
m_pszDisplayName = PszLoadString(IDS_UNKNOWN_PERSON); }
m_dwTick = ::GetTickCount();
DbgMsgCall("CCall: %08X Created Name=[%s] CallTo=[%s]", this, m_pszDisplayName, m_pszCallTo ? m_pszCallTo : _TEXT("<NULL>"));
// add it to the global call list
if (NULL == g_pCallList) { g_pCallList = new COBLIST; if (NULL == g_pCallList) { ERROR_OUT(("CCall::CCall - unable to allocate g_pCallList")); return; } } m_pos = g_pCallList->AddTail(this);
}
CCall::~CCall() { DBGENTRY(CCall::~CCall);
RemoveFromList();
delete m_pszDisplayName; delete m_pszCallTo;
if(m_pInternalICall) { m_pInternalICall->Release(); m_pInternalICall = NULL; }
DbgMsg(iZONE_OBJECTS, "Obj: %08X destroyed CCall", this); DBGEXIT(CCall::~CCall); }
/* S E T N M C A L L */ /*-------------------------------------------------------------------------
%%Function: SetNmCall -------------------------------------------------------------------------*/ VOID CCall::SetNmCall(INmCall * pCall) { ASSERT(NULL != pCall); ASSERT((!m_pInternalICall) || (m_pInternalICall == pCall)); if(!m_pInternalICall) { pCall->AddRef(); m_pInternalICall = pCall; }
if(!m_dwCookie) { NmAdvise(m_pInternalICall, this, IID_INmCallNotify2, &m_dwCookie); }
Update(); }
BOOL CCall::RemoveFromList(void) { // Remove the call from the global list
if (NULL == m_pos) return FALSE;
ASSERT(NULL != g_pCallList); CCall * pCall = (CCall *) g_pCallList->RemoveAt(m_pos); ASSERT(this == pCall); m_pos = NULL;
if(m_pInternalICall) { NmUnadvise(m_pInternalICall, IID_INmCallNotify2, m_dwCookie); m_dwCookie = NULL; }
return TRUE; }
VOID CCall::Cancel(BOOL fDisplayCancelMsg) { if (!FComplete()) { if (fDisplayCancelMsg & !FIncoming()) { DisplayPopup(); // Inform the user with a small popup message
} if (m_pInternalICall) { m_pInternalICall->Cancel(); }
Update(); } }
///////////////////////////////////////////////////////////////////////////
// IUnknown methods
STDMETHODIMP_(ULONG) CCall::AddRef(void) { return RefCount::AddRef(); } STDMETHODIMP_(ULONG) CCall::Release(void) { return RefCount::Release(); }
STDMETHODIMP CCall::QueryInterface(REFIID riid, PVOID *ppv) { HRESULT hr = S_OK;
if ((riid == IID_INmCallNotify2) || (riid == IID_INmCallNotify) || (riid == IID_IUnknown)) { *ppv = (INmCallNotify2 *)this; ApiDebugMsg(("CCall::QueryInterface()")); } else { hr = E_NOINTERFACE; *ppv = NULL; ApiDebugMsg(("CCall::QueryInterface(): Called on unknown interface.")); }
if (S_OK == hr) { AddRef(); }
return hr; }
///////////////////////////////////////////////////////////////////////////
// INmCallNotify methods
STDMETHODIMP CCall::NmUI(CONFN uNotify) { return S_OK; }
STDMETHODIMP CCall::StateChanged(NM_CALL_STATE uState) { Update();
return S_OK; }
STDMETHODIMP CCall::Failed(ULONG uError) { DbgMsgCall("CCall: %08X Failed uError=%d", this, uError); return S_OK; }
STDMETHODIMP CCall::Accepted(INmConference *pConference) { DbgMsgCall("CCall: %08X Accepted pConference=0x%08X", this, pConference); return S_OK; }
VOID CCall::ShowProgress(BOOL fShow) { if (NULL == m_pDlgCall) return;
ShowWindow(m_pDlgCall->GetHwnd(), fShow ? SW_SHOWNORMAL : SW_HIDE); }
VOID CCall::RemoveProgress(void) { if (NULL == m_pDlgCall) return;
m_pDlgCall->Destroy(); m_pDlgCall->Release(); m_pDlgCall = NULL; }
/* C A L L E R R O R */ /*-------------------------------------------------------------------------
%%Function: CallError -------------------------------------------------------------------------*/ STDMETHODIMP CCall::CallError(UINT cns) { UINT ids = 0;
ShowProgress(FALSE);
ASSERT(m_pInternalICall != NULL); DbgMsgCall("CCall: %08X CallError cns=%08X", this, cns);
// Translate cns to normal error message
switch (cns) { case CN_RC_NAME_RESOLUTION_FAILED: ids = IDS_RESOLVE_FAILED; break;
case CN_RC_CONNECT_FAILED: case CN_RC_AUDIO_CONNECT_FAILED: ids = IDS_COULD_NOT_CONNECT; break; case CN_RC_CONNECT_REMOTE_NO_SECURITY: ids = IDS_CONNECT_REMOTE_NO_SECURITY; break;
case CN_RC_CONNECT_REMOTE_DOWNLEVEL_SECURITY: ids = IDS_CONNECT_REMOTE_DOWNLEVEL_SECURITY; break;
case CN_RC_CONNECT_AUTHENTICATION_FAILED: ids = IDS_CONNECT_AUTHENTICATION_FAILED; break;
case CN_RC_SECURITY_FAILED: ids = IDS_CONNECT_SECURITY_FAILED; break;
case CN_RC_CONNECT_REMOTE_REQUIRE_SECURITY: ids = IDS_CONNECT_REMOTE_REQUIRE_SECURITY; break; case CN_RC_CONFERENCE_JOIN_DENIED: ids = IDS_JOIN_DENIED; break; case CN_RC_CONFERENCE_INVITE_DENIED: ids = IDS_INVITE_DENIED; break; case CN_RC_INVITE_DENIED_REMOTE_IN_CONF: ids = IDS_INVITE_DENIED_REMOTE_CONF; break; case CN_RC_CONFERENCE_DOES_NOT_EXIST: ids = m_fSelectedConference ? IDS_CONFERENCE_DOES_NOT_EXIST : IDS_CONFERENCE_ENDED_BEFORE_JOIN; break; case CN_RC_CONFERENCE_ENDED_BEFORE_JOIN: // REVIEW: This is no longer sent?
ids = IDS_CONFERENCE_ENDED_BEFORE_JOIN; break; case CN_RC_AUDIO_NOT_AVAILABLE: ids = IDS_AUDIO_NOT_AVAILABLE; break; case CN_RC_AUDIO_FAILED_AFTER_DATA: ids = IDS_AUDIO_FAILED_AFTER_DATA; break; case CN_RC_AUDIO_IN_USE_REMOTE_AFTER_DATA: ids = IDS_AUDIO_IN_USE_REMOTE_AFTER_DATA; break; case CN_RC_AUDIO_IN_USE_REMOTE: ids = IDS_AUDIO_IN_USE_REMOTE; break; case CN_RC_AUDIO_IN_USE_LOCAL_AFTER_DATA: ids = IDS_AUDIO_IN_USE_LOCAL_AFTER_DATA; break; case CN_RC_AUDIO_IN_USE_LOCAL: ids = IDS_AUDIO_IN_USE_LOCAL; break; case CN_RC_CANT_INVITE_MCU: ids = IDS_CANT_INVITE_MCU; break; case CN_RC_REMOTE_PLACING_CALL: ids = IDS_REMOTE_PLACING_CALL; break; case CN_RC_TRANSPORT_FAILURE: ids = IDS_TRANSPORT_UNAVAILABLE; break;
case CN_RC_CANT_JOIN_ALREADY_IN_CALL: ids = IDS_INCALL_JOIN_FAILED; break;
case CN_RC_CONFERENCE_ENDED_BEFORE_ACCEPTED: ids = IDS_INVITE_CONF_ENDED; break;
case CN_RC_GK_CALLEE_NOT_REGISTERED: ids = IDS_GK_CALLEE_NOT_REGISTERED; break;
case CN_RC_GK_TIMEOUT: ids = IDS_GK_TIMEOUT; break;
case CN_RC_GK_REJECTED: ids = IDS_GK_REJECTED; break;
case CN_RC_GK_NOT_REGISTERED: ids = IDS_GK_NOT_REGISTERED; break;
default: return S_FALSE; } /* switch (cns) */
DisplayMsgIdsParam(ids, m_pszDisplayName); return S_OK; }
STDMETHODIMP CCall::RemoteConference(BOOL fMCU, BSTR *pwszConfNames, BSTR *pbstrConfToJoin) { return OnUIRemoteConference(fMCU, (PWSTR *)pwszConfNames, pbstrConfToJoin); }
STDMETHODIMP CCall::RemotePassword(BSTR bstrConference, BSTR *pbstrPassword, PBYTE pb, DWORD cb, BOOL fIsService) { TCHAR* pLastCertText = NULL; if (NULL != pb) { ASSERT(cb > 0); if (!(pLastCertText = FormatCert(pb, cb))) { ERROR_OUT(("FormatCert failed")); } }
ShowProgress(FALSE); HRESULT hr = OnUIRemotePassword(bstrConference, pbstrPassword, pLastCertText, fIsService); if (pLastCertText) delete pLastCertText; return hr; }
NM_CALL_STATE CCall::GetState() { NM_CALL_STATE callState = NM_CALL_INVALID;
if(m_pInternalICall) { m_pInternalICall->GetState(&callState); }
return callState; }
/* U P D A T E */ /*-------------------------------------------------------------------------
%%Function: Update
Update the cached information about the call -------------------------------------------------------------------------*/ VOID CCall::Update(void) { DBGENTRY(CCall::Update); NM_CALL_STATE callState = GetState();;
switch (callState) { case NM_CALL_CANCELED: case NM_CALL_ACCEPTED: case NM_CALL_REJECTED: // Remove the call from the global list because we'll never get
// any more notifications for this.
if (RemoveFromList()) { if (FIncoming()) { if ((NM_CALL_CANCELED == callState) && (NULL != m_ppm)) { //
// if m_fInRespond is set then we've already
// dismissed the dialog and the call is being
// cancelled because we discovered the underlying
// connection is gone.
//
if ( !m_fInRespond ) { delete m_ppm; m_ppm = NULL;
// Release the lock added by OnRing
Release(); } } LogCall(NM_CALL_ACCEPTED == callState); } else { RemoveProgress();
if (NM_CALL_ACCEPTED == callState) {
if(m_bAddToMru) { CAcdMru CallList; CallList.AddEntry(m_pszDisplayName, m_pszCallTo, m_nmAddrType); CallList.Save(); } } }
// Release the initial lock on this object
Release(); } break;
case NM_CALL_RING: OnRing(); break;
case NM_CALL_SEARCH: ASSERT(NULL == m_pDlgCall); m_pDlgCall = new CDlgCall(this); break;
case NM_CALL_WAIT: if (NULL != m_pDlgCall) { m_pDlgCall->OnStateChange(); } break;
default: ERROR_OUT(("CCall::Update: Unknown state %08X", callState));
case NM_CALL_INVALID: case NM_CALL_INIT: break; }
::UpdateUI(CRUI_CALLANIM | CRUI_TOOLBAR | CRUI_STATUSBAR);
DBGEXIT(CCall::Update); }
/* F C O M P L E T E */ /*-------------------------------------------------------------------------
%%Function: FComplete Return TRUE if the call has completed -------------------------------------------------------------------------*/ BOOL CCall::FComplete(void) { switch (GetState()) { case NM_CALL_ACCEPTED: case NM_CALL_REJECTED: case NM_CALL_CANCELED: return TRUE;
case NM_CALL_INVALID: case NM_CALL_INIT: case NM_CALL_RING: case NM_CALL_SEARCH: case NM_CALL_WAIT: default: return FALSE; } }
/* P O P U P M S G R I N G I N G C A L L B A C K */ /*-------------------------------------------------------------------------
%%Function: CCall::PopupMsgRingingCallback
-------------------------------------------------------------------------*/
VOID CALLBACK CCall::PopupMsgRingingCallback(LPVOID pContext, DWORD dwFlags) { CCall *pCall = (CCall *) pContext; ASSERT(NULL != pCall);
DbgMsgCall("CCall: %08X Responding from invite popup - result is 0x%08X", pCall, dwFlags); DWORD dwCLEF;
if(!( PMF_KILLED & dwFlags )) { dwCLEF = (PMF_OK & dwFlags) ? CLEF_ACCEPTED : CLEF_REJECTED;
if (PMF_TIMEOUT & dwFlags) { dwCLEF |= CLEF_TIMED_OUT; }
pCall->RespondToRinging(dwCLEF); }
if(pCall->m_ppm) { // pop up message will be destroyed after callback returns
pCall->m_ppm = NULL;
// Release the lock added by OnRing
pCall->Release(); } }
/* O N R I N G */ /*-------------------------------------------------------------------------
%%Function: OnRing
Handling an incoming call that just started to "ring". -------------------------------------------------------------------------*/ VOID CCall::OnRing(void) { DbgMsgCall("CCall: %08X OnRing", this); if (FRejectIncomingCalls()) { // Respond negatively
WARNING_OUT(("Rejecting invite - not listening or sys pol disabled")); RespondToRinging(CLEF_REJECTED); return; }
if( ConfPolicies::IsAutoAcceptCallsEnabled() && !_Module.InitControlMode()) { // Respond with success
RespondToRinging(CLEF_ACCEPTED | CLEF_AUTO_ACCEPTED); return; }
if(!_Module.InitControlMode()) { // Display a message for the user
TCHAR szFormatBuf[MAX_PATH]; TCHAR szMsgBuf[MAX_PATH];
if (FLoadString(IDS_INVITE_PERMISSION, szFormatBuf, CCHMAX(szFormatBuf))) { TCHAR szName[MAX_PATH]; LPTSTR psz = m_pszDisplayName; if (FEmptySz(psz)) { // The name string is blank, so fill it in with a default:
::LoadString(::GetInstanceHandle(), IDS_UNKNOWN_PERSON, szName, CCHMAX(szName)); psz = szName; } wsprintf(szMsgBuf, szFormatBuf, psz); }
ASSERT(NULL == m_ppm); m_ppm = new CPopupMsg(PopupMsgRingingCallback, this); if (NULL != m_ppm) { RegEntry re(UI_KEY, HKEY_CURRENT_USER); UINT uTime = re.GetNumber(REGVAL_RING_TIMEOUT, DEFAULT_RING_TIMEOUT) * 1000; AddRef(); // Released in PopupMsgRingingCallback
m_ppm->CreateDlg(szMsgBuf, TRUE, MAKEINTRESOURCE(IDI_CONFROOM), ::GetInstanceHandle(), IDS_INVITE_SOUND, uTime); } } }
/* R E S P O N D T O R I N G I N G */ /*-------------------------------------------------------------------------
%%Function: RespondToRinging -------------------------------------------------------------------------*/ BOOL CCall::RespondToRinging(DWORD dwCLEF) { BOOL fAccept = FALSE; m_fInRespond = TRUE; if (NM_CALL_RING == GetState()) { if (!FIsConfRoomClosing() && (CLEF_ACCEPTED & dwCLEF)) { fAccept = TRUE; if(_Module.IsUIActive()) { CConfRoom * pcr = ::GetConfRoom(); ASSERT(pcr); pcr->BringToFront(); } }
CNmCallObj::StateChanged(m_pInternalICall, fAccept ? NM_CALL_ACCEPTED : NM_CALL_REJECTED);
if (fAccept) { m_pInternalICall->Accept(); } else { m_pInternalICall->Reject(); } } else { CallError(CN_RC_CONFERENCE_ENDED_BEFORE_ACCEPTED); }
m_fInRespond = FALSE; return fAccept; }
/* D I S P L A Y P O P U P */ /*-------------------------------------------------------------------------
%%Function: DisplayPopup -------------------------------------------------------------------------*/ VOID CCall::DisplayPopup(void) { CPopupMsg* ppm = new CPopupMsg(NULL); if (NULL == ppm) return; TCHAR szMsg[MAX_PATH*2]; if (FLoadString1(IDS_CALL_CANCELED_FORMAT, szMsg, m_pszDisplayName)) { ppm->Create(szMsg, FALSE, MAKEINTRESOURCE(IDI_CONFROOM), ::GetInstanceHandle(), IDS_PERSON_LEFT_SOUND, ROSTER_TIP_TIMEOUT); } else { delete ppm; }
}
/* P L A C E C A L L */ /*-------------------------------------------------------------------------
%%Function: PlaceCall
Place an outgoing call. -------------------------------------------------------------------------*/ HRESULT CCall::PlaceCall ( DWORD dwFlags, NM_ADDR_TYPE addrType, const TCHAR * const setupAddress, const TCHAR * const destinationAddress, const TCHAR * const alias, const TCHAR * const url, const TCHAR * const conference, const TCHAR * const password, const TCHAR * const userData ){
DBGENTRY(CCall::PlaceCall); HRESULT hr = E_FAIL;
ASSERT(m_pInternalICall == NULL);
INmManager2 *pNmMgr = CConfMan::GetNmManager(); ASSERT (NULL != pNmMgr);
hr = pNmMgr->CallEx( &m_pInternalICall, dwFlags, addrType, CComBSTR( GetPszName() ), CComBSTR( setupAddress ), CComBSTR( destinationAddress ), CComBSTR( alias ), CComBSTR( url ), CComBSTR( conference ), CComBSTR( password ), CComBSTR( userData ) );
if(m_pInternalICall && (CRPCF_JOIN & dwFlags) ) { SetSelectedConference(); }
// Force an update of the status bar, animation, etc.
::UpdateUI(CRUI_DEFAULT);
pNmMgr->Release();
TRACE_OUT(("CCall::PlaceCall(%s) result=%08X", m_pszCallTo? m_pszCallTo: g_szEmpty, hr));
DBGEXIT_HR(CCall::PlaceCall, hr); return hr; }
VOID CCall::LogCall(BOOL fAccepted) { LPCTSTR pcszName = GetPszName();
TCHAR szName[MAX_PATH]; if (FEmptySz(pcszName)) { if (FLoadString(IDS_UNKNOWN_PERSON, szName, CCHMAX(szName))) pcszName = szName; }
LOGHDR logHdr; CRosterInfo ri; LPBYTE pb; ULONG cb; LPBYTE pbCert = NULL; ULONG cbCert = 0; if (SUCCEEDED(GetINmCall()->GetUserData(g_csguidRostInfo, &pb, &cb))) { ri.Load(pb); } GetINmCall()->GetUserData(g_csguidSecurity, &pbCert, &cbCert); DWORD dwCLEF = fAccepted ? CLEF_ACCEPTED : CLEF_REJECTED; if (ri.IsEmpty()) { // No caller data - not NetMeeting
dwCLEF |= CLEF_NO_CALL; } if (pbCert) { ASSERT(cbCert); dwCLEF |= CLEF_SECURE; }
ZeroMemory(&logHdr, sizeof(LOGHDR)); logHdr.dwCLEF = dwCLEF;
if (NULL != ::GetIncomingCallLog() ) { // Write the data to the log file
::GetIncomingCallLog()->AddCall(pcszName, &logHdr, &ri, pbCert, cbCert); } }
/* C R E A T E I N C O M I N G C A L L */ /*-------------------------------------------------------------------------
%%Function: CreateIncomingCall
Create an CCall object for the incoming call. -------------------------------------------------------------------------*/ CCall * CreateIncomingCall(INmCall * pNmCall) { HRESULT hr; BSTR bstr; LPTSTR pszName = NULL; LPTSTR pszAddr = NULL; NM_ADDR_TYPE addrType = NM_ADDR_UNKNOWN;
ASSERT(NULL != pNmCall);
// Get the display name
hr = pNmCall->GetName(&bstr); if (SUCCEEDED(hr)) { hr = BSTR_to_LPTSTR(&pszName, bstr); SysFreeString(bstr); }
// Get the address and type
hr = pNmCall->GetAddr(&bstr, &addrType); if (SUCCEEDED(hr)) { hr = BSTR_to_LPTSTR(&pszAddr, bstr); SysFreeString(bstr); }
CCall * pCall = new CCall(pszAddr, pszName, NM_ADDR_CALLTO, FALSE, TRUE /* fIncoming */);
delete pszName; delete pszAddr;
return pCall; }
///////////////////////////////////////////////////////////////////////////
// Global Functions
/* C A L L F R O M N M C A L L */ /*-------------------------------------------------------------------------
%%Function: CallFromNmCall -------------------------------------------------------------------------*/ CCall * CallFromNmCall(INmCall * pNmCall) { if (NULL == g_pCallList) return NULL;
POSITION pos = g_pCallList->GetHeadPosition(); while (pos) { CCall * pCall = (CCall *) g_pCallList->GetNext(pos); ASSERT(NULL != pCall); if (pNmCall == pCall->GetINmCall()) { return pCall; } }
// no matching call?
return NULL; }
/* F I S C A L L I N P R O G R E S S */ /*-------------------------------------------------------------------------
%%Function: FIsCallInProgress
Return TRUE if there is an incoming or outgoing call in progress. -------------------------------------------------------------------------*/ BOOL FIsCallInProgress(void) { if (NULL == g_pCallList) return FALSE;
return !g_pCallList->IsEmpty(); }
/* G E T L A S T O U T G O I N G C A L L */ /*-------------------------------------------------------------------------
%%Function: GetLastOutgoingCall -------------------------------------------------------------------------*/ CCall * GetLastOutgoingCall(void) { if (NULL == g_pCallList) return NULL;
CCall * pCall = NULL; POSITION pos = g_pCallList->GetHeadPosition(); while (pos) { CCall * pCallTemp = (CCall *) g_pCallList->GetNext(pos); ASSERT(NULL != pCallTemp); if (!pCallTemp->FIncoming()) { pCall = pCallTemp; } }
return pCall; }
/* G E T C A L L S T A T U S */ /*-------------------------------------------------------------------------
%%Function: GetCallStatus
Check the current call status and return a string for the status bar. Return 0 if no call information is available -------------------------------------------------------------------------*/ DWORD GetCallStatus(LPTSTR pszStatus, int cchMax, UINT * puID) { ASSERT(NULL != pszStatus); ASSERT(NULL != puID); ASSERT(cchMax > 0);
*pszStatus = _T('\0'); *puID = 0;
CCall *pCall = GetLastOutgoingCall(); if (NULL == pCall) return 0; // not in a call
// Use the status info from the most recent connection attempt:
switch (pCall->GetState()) { case NM_CALL_INIT: { *puID = IDS_STATUS_SETTING_UP; break; }
case NM_CALL_SEARCH: { *puID = IDS_STATUS_FINDING; break; }
case NM_CALL_WAIT: { *puID = IDS_STATUS_WAITING; break; }
default: { // unknown/useless call state
return 0; } } /* switch */
if (FEmptySz(pCall->GetPszName())) { return 0; }
if (!FLoadString1(*puID, pszStatus, pCall->GetPszName())) { return 0; }
return pCall->GetTickCount(); }
///////////////////////////////////////////////////////////////////////////
// TODO: Replace these with real connection points
/* O N U I C A L L C R E A T E D */ /*-------------------------------------------------------------------------
%%Function: OnUICallCreated -------------------------------------------------------------------------*/ HRESULT OnUICallCreated(INmCall *pNmCall) { CCall * pCall;
// Notify the API
if (S_OK == pNmCall->IsIncoming()) { pCall = CreateIncomingCall(pNmCall); if (NULL == pCall) { return S_FALSE; } } else { pCall = CallFromNmCall(pNmCall); if (NULL == pCall) { WARNING_OUT(("OnUiCallCreated: Unable to find outgoing call=%08X", pNmCall)); return S_FALSE; } } pCall->SetNmCall(pNmCall);
return S_OK; }
HRESULT OnUIRemotePassword(BSTR bstrConference, BSTR * pbstrPassword, LPCTSTR pCertText, BOOL fIsService) { HRESULT hr = S_FALSE;
LPTSTR szName; hr = BSTR_to_LPTSTR (&szName, bstrConference); if (SUCCEEDED(hr)) { CPasswordDlg dlgPw(::GetMainWindow(), szName, pCertText, fIsService); // Free resources
//
delete (szName);
if (IDOK == dlgPw.DoModal()) { TRACE_OUT(("password dialog complete (OK pressed)"));
LPTSTR_to_BSTR(pbstrPassword, dlgPw.GetPassword()); hr = S_OK; } }
return hr; }
HRESULT CCall::OnUIRemoteConference(BOOL fMCU, PWSTR* pwszConfNames, BSTR *pbstrConfToJoin) { HRESULT hr = S_FALSE;
ShowProgress(FALSE);
// We bring up the "choose a conference" dialog
// when calling an MCU or another node with more than
// one "listed" conference
CChooseConfDlg dlgChoose(::GetMainWindow(), pwszConfNames); if (IDOK == dlgChoose.DoModal()) { TRACE_OUT(("choose conference dialog complete (OK pressed)")); LPTSTR_to_BSTR(pbstrConfToJoin, dlgChoose.GetName()); hr = S_OK; m_fSelectedConference = TRUE; }
ShowProgress(TRUE);
return hr; }
/* F R E E C A L L L I S T */ /*-------------------------------------------------------------------------
%%Function: FreeCallList
Free any remaining calls -------------------------------------------------------------------------*/ VOID FreeCallList(void) { if (NULL == g_pCallList) return;
while (!g_pCallList->IsEmpty()) { CCall * pCall = (CCall *) g_pCallList->GetHead(); WARNING_OUT(("FreeCallList: Orphan call=%08X", pCall)); pCall->RemoveFromList(); pCall->Release(); }
delete g_pCallList; g_pCallList = NULL; }
/* C A N C E L A L L O U T G O I N G C A L L S */ /*-------------------------------------------------------------------------
%%Function: CancelAllOutgoingCalls -------------------------------------------------------------------------*/ VOID CancelAllOutgoingCalls(void) { if (NULL == g_pCallList) return;
POSITION pos = g_pCallList->GetHeadPosition(); while (pos) { CCall * pCall = (CCall *) g_pCallList->GetNext(pos); ASSERT(NULL != pCall); if (!pCall->FIncoming()) { // Cancel will release the call object.
// Ensure that there is at least one reference.
pCall->AddRef(); pCall->Cancel(TRUE); pCall->Release(); } } }
/* C A N C E L A L L C A L L S */ /*-------------------------------------------------------------------------
%%Function: CancelAllCalls -------------------------------------------------------------------------*/ VOID CancelAllCalls(void) { if (NULL == g_pCallList) return;
POSITION pos = g_pCallList->GetHeadPosition(); while (pos) { CCall * pCall = (CCall *) g_pCallList->GetNext(pos); ASSERT(NULL != pCall); // Cancel will release the call object.
// Ensure that there is at least one reference.
pCall->AddRef(); pCall->Cancel(TRUE); pCall->Release(); } }
///////////////////////////////////////////////////////////////////////////
// IP Utilities
/* F L O C A L I P A D D R E S S */ /*-------------------------------------------------------------------------
%%Function: FLocalIpAddress
Return TRUE if the parameter matches the local IP address. -------------------------------------------------------------------------*/ BOOL FLocalIpAddress(DWORD dwIP) { if (dwIP == 0x0100007F) { WARNING_OUT(("t-bkrav is trying to call himself")); return TRUE; }
// Get own host name
TCHAR sz[MAX_PATH]; if (0 != gethostname(sz, CCHMAX(sz))) { WARNING_OUT(("FLocalIpAddress: gethostname failed? err=%s", PszWSALastError())); return FALSE; }
HOSTENT * pHostInfo = gethostbyname(sz); if (NULL == pHostInfo) { WARNING_OUT(("FLocalIpAddress: gethostbyname failed? err=%s", PszWSALastError())); return FALSE; }
return (dwIP == *(DWORD *) pHostInfo->h_addr); }
/* F I P A D D R E S S */ /*-------------------------------------------------------------------------
%%Function: FIpAddress
Return TRUE if the string is in the form: a.b.c.d where a,b,c,d < 256.
Note that inet_addr returns success on strings like "55534" and "3102.550"
FUTURE: Return the converted DWORD -------------------------------------------------------------------------*/ BOOL FIpAddress(LPCTSTR pcsz) { TCHAR ch; int cPeriods = 0; int uVal = 0;
ASSERT(NULL != pcsz); while (_T('\0') != (ch = *pcsz++)) { switch (ch) { case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'): case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'): uVal = (uVal *= 10) + (ch - _T('0')); if (uVal > 255) return FALSE; break;
case _T('.'): cPeriods++; uVal = 0; break;
default: return FALSE; } /* switch (ch) */ } return (3 == cPeriods); }
VOID DisplayCallError(HRESULT hr, LPCTSTR pcszName) { int ids;
WARNING_OUT(("DisplayCallError pcsz=[%s] err=%s", pcszName, PszHResult(hr)));
switch (hr) { case S_OK: case S_FALSE: return; // no error
default: case E_FAIL: WARNING_OUT(("DisplayCallError - message is not very informative. HRESULT=%08X", hr)); // fall thru to IDS_RESOLVE_FAILED
case NM_CALLERR_NAME_RESOLUTION: ids = IDS_RESOLVE_FAILED; break;
case NM_CALLERR_NOT_INITIALIZED: case NM_CALLERR_NOT_FOUND: ids = IDS_COULD_NOT_INVITE; break;
case NM_CALLERR_LOOPBACK: ids = IDS_CALL_LOOPBACK; break;
case NM_CALLERR_ALREADY_CALLING: ids = IDS_ALREADY_CALLING; break;
case E_OUTOFMEMORY: ids = IDS_ULSLOGON_OUTOFMEMORY; break;
case NM_CALLERR_INVALID_PHONE_NUMBER: ids = IDS_CALLERR_E_BAD_PHONE_NUMBER; break;
case NM_CALLERR_NO_PHONE_SUPPORT: ids = IDS_CALLERR_E_NO_PHONE_SUPPORT; break;
case NM_CALLERR_INVALID_IPADDRESS: ids = IDS_CALLERR_E_BAD_IPADDRESS; break;
case NM_CALLERR_HOST_RESOLUTION_FAILED: ids = IDS_CALLERR_E_BAD_HOSTNAME; break;
case NM_CALLERR_NO_ILS: ids = IDS_CALLERR_E_NO_ILS; break;
case NM_CALLERR_ILS_RESOLUTION_FAILED: ids = IDS_CALLERR_E_ILS_RESOLUTION_FAILED; break;
case NM_CALLERR_NO_ADDRESS: ids = IDS_CALLERR_E_NO_ADDRESS; break;
case NM_CALLERR_INVALID_ADDRESS: ids = IDS_CALLERR_E_INVALID_ADDRESS; break;
case NM_CALLERR_NO_GATEKEEPER: ids = IDS_CALLERR_E_NO_GATEKEEPER; break;
case NM_CALLERR_NOT_REGISTERED: ids = IDS_GK_NOT_REGISTERED; break;
case NM_CALLERR_NO_GATEWAY: ids = IDS_CALLERR_E_NO_GATEWAY; break;
case NM_CALLERR_PARAM_ERROR: ids = IDS_CALLERR_E_PARAM_ERROR; break;
case NM_CALLERR_SECURITY_MISMATCH: ids = IDS_CALLERR_E_SECURITY_MISMATCH; break;
case NM_CALLERR_UNESCAPE_ERROR: ids = IDS_CALLERR_E_UNESCAPE_ERROR; break;
case NM_CALLERR_IN_CONFERENCE: ids = IDS_INVITE_DENIED_REMOTE_CONF; break; }
DisplayMsgIdsParam(ids, pcszName); }
///////////////////////////////////////////////////////////////////////
// Gateway utility routines
/* G E T D E F A U L T G A T E W A Y */ /*-------------------------------------------------------------------------
%%Function: GetDefaultGateway -------------------------------------------------------------------------*/ int GetDefaultGateway(LPTSTR psz, UINT cchMax) { RegEntry re(CONFERENCING_KEY, HKEY_CURRENT_USER);
// Make sure it's enabled
if (0 == re.GetNumber(REGVAL_USE_H323_GATEWAY, DEFAULT_USE_H323_GATEWAY)) { SetEmptySz(psz); return 0; }
lstrcpyn(psz, re.GetString(REGVAL_H323_GATEWAY), cchMax); return lstrlen(psz); }
BOOL FH323GatewayEnabled(VOID) { if (!::FIsAudioAllowed()) return FALSE;
TCHAR sz[MAX_PATH]; return 0 != GetDefaultGateway(sz, CCHMAX(sz)); }
/* C R E A T E G A T E W A Y A D D R E S S */ /*-------------------------------------------------------------------------
%%Function: CreateGatewayAddress
Create a gateway address in the form: gateway/address e.g. "157.59.0.40/65000" -------------------------------------------------------------------------*/ HRESULT CreateGatewayAddress(LPTSTR pszResult, UINT cchMax, LPCTSTR pszAddr) { int cch = GetDefaultGateway(pszResult, cchMax); if (0 == cch) return E_FAIL; if (cchMax <= (UINT) (cch + 1 + lstrlen(pszAddr))) return E_FAIL;
*(pszResult+cch) = _T('/');
pszResult += cch+1; lstrcpy(pszResult, pszAddr); return S_OK; }
///////////////////////////////////////////////////////////////////////
// Gatekeeper utility routines
NM_GK_STATE g_GkLogonState = NM_GK_NOT_IN_GK_MODE;
BOOL FGkEnabled(VOID) { return ( ConfPolicies::CallingMode_GateKeeper == ConfPolicies::GetCallingMode() ); }
inline bool ISE164CHAR(TCHAR digit) { if ((digit >= '0') && (digit <= '9')) { return true; } if ((digit == '#') || (digit == '*')) { return true; }
return false; }
// removes non E-164 chars from a phone number string
int CleanupE164String(LPTSTR szPhoneNumber) { int nLength; int nIndex, nIndexWrite;
if ((szPhoneNumber == NULL) || (szPhoneNumber[0] == '\0')) { return 0; }
nIndexWrite = 0; nLength = lstrlen(szPhoneNumber); for (nIndex = 0; nIndex < nLength; nIndex++) { if (ISE164CHAR(szPhoneNumber[nIndex])) { if (nIndex != nIndexWrite) { szPhoneNumber[nIndexWrite] = szPhoneNumber[nIndex]; } nIndexWrite++; } }
szPhoneNumber[nIndexWrite] = '\0';
return nIndexWrite; // length of the new string
}
// removes non E-164 & non-comma chars from a phone number string
int CleanupE164StringEx(LPTSTR szPhoneNumber) { int nLength; int nIndex, nIndexWrite;
if ((szPhoneNumber == NULL) || (szPhoneNumber[0] == '\0')) { return 0; }
nIndexWrite = 0; nLength = lstrlen(szPhoneNumber); for (nIndex = 0; nIndex < nLength; nIndex++) { if (ISE164CHAR(szPhoneNumber[nIndex]) || (szPhoneNumber[nIndex] == ',') ) { if (nIndex != nIndexWrite) { szPhoneNumber[nIndexWrite] = szPhoneNumber[nIndex]; } nIndexWrite++; } }
szPhoneNumber[nIndexWrite] = '\0';
return nIndexWrite; // length of the new string
}
static bool _CanLogonToGk() { return (NULL != g_pNmSysInfo) && FGkEnabled() && ( ( NM_GK_IDLE == g_GkLogonState ) || ( NM_GK_NOT_IN_GK_MODE == g_GkLogonState ) );
}
void GkLogon(void) { if(_CanLogonToGk()) { // In case the logon fails, we set this to idle
SetGkLogonState(NM_GK_IDLE);
RegEntry reConf(CONFERENCING_KEY, HKEY_CURRENT_USER); LPCTSTR pszServer = reConf.GetString(REGVAL_GK_SERVER); g_pCCallto->SetGatekeeperName( pszServer );
RegEntry reULS(ISAPI_CLIENT_KEY, HKEY_CURRENT_USER); LPTSTR pszAliasID = NULL; LPTSTR pszAliasE164 = NULL;
ConfPolicies::eGKAddressingMode mode = ConfPolicies::GKAddressing_Invalid;
mode = ConfPolicies::GetGKAddressingMode();
if( (ConfPolicies::GKAddressing_PhoneNum == mode) || (ConfPolicies::GKAddressing_Both == mode) ) { pszAliasE164 = PszAlloc(reULS.GetString( REGVAL_ULS_PHONENUM_NAME ));
CleanupE164String(pszAliasE164); }
if( (ConfPolicies::GKAddressing_Account == mode) || (ConfPolicies::GKAddressing_Both == mode) ) { pszAliasID = PszAlloc(reULS.GetString( REGVAL_ULS_GK_ACCOUNT )); }
HRESULT hr = g_pNmSysInfo->GkLogon(CComBSTR(pszServer), CComBSTR(pszAliasID ? pszAliasID : g_szEmpty), CComBSTR(pszAliasE164 ? pszAliasE164 : g_szEmpty));
delete pszAliasID; delete pszAliasE164; if( SUCCEEDED( hr ) ) { SetGkLogonState(NM_GK_LOGGING_ON); } else { PostConfMsgBox(IDS_ERR_GK_NOT_FOUND); } } }
void GkLogoff(void) { if (NULL != g_pNmSysInfo) { g_pNmSysInfo->GkLogoff(); }
SetGkLogonState( NM_GK_IDLE ); }
bool IsGatekeeperLoggedOn(void) { return ( NM_GK_LOGGED_ON == g_GkLogonState );
}
bool IsGatekeeperLoggingOn(void) { return ( NM_GK_LOGGING_ON == g_GkLogonState ); }
void SetGkLogonState( NM_GK_STATE state ) {
if( FGkEnabled() ) { if( g_GkLogonState != state ) { // Set the new state
g_GkLogonState = state; } } else { // We are not in GK mode anymore
g_GkLogonState = NM_GK_NOT_IN_GK_MODE; }
::UpdateUI(CRUI_STATUSBAR, TRUE);
g_pCCallto->SetGatekeeperEnabled( IsGatekeeperLoggedOn() || IsGatekeeperLoggingOn() ); }
CCallResolver::CCallResolver(LPCTSTR pszAddr, NM_ADDR_TYPE addrType) : m_pszAddr(PszAlloc(pszAddr)), m_pszAddrIP(NULL), m_addrType(addrType) { }
CCallResolver::~CCallResolver() { delete m_pszAddr; delete m_pszAddrIP; }
///////////////////////////////////////////////////////////////////////////
// Name Resolution
HRESULT CCallResolver::CheckHostEnt(HOSTENT * pHostInfo) { // Only expecting IP addresses..
if ((AF_INET != pHostInfo->h_addrtype) || (sizeof(DWORD) != pHostInfo->h_length)) { WARNING_OUT(("CCallResolver: %08X CheckHostEnt - address type=%d",this, pHostInfo->h_addrtype)); return E_FAIL; }
struct in_addr inAddr; inAddr.s_addr = *((DWORD *)pHostInfo->h_addr); if (FLocalIpAddress(inAddr.s_addr)) { WARNING_OUT(("CCallResolver: %08X CheckHostEnt - Attempted to call local machine", this)); return NM_CALLERR_LOOPBACK; }
m_pszAddrIP = PszAlloc(inet_ntoa(inAddr)); if (NULL == m_pszAddrIP) return E_OUTOFMEMORY;
return S_OK; }
HRESULT CCallResolver::ResolveMachineName(LPCTSTR pcszAddr) { TCHAR szOem[MAX_PATH]; lstrcpyn(szOem, pcszAddr, CCHMAX(szOem)); CharUpper(szOem); CharToOem(szOem, szOem);
HOSTENT * pHostInfo = gethostbyname(szOem); if (NULL == pHostInfo) { WARNING_OUT(("CCallResolver: %08X ResolveMachineName(%s) gethostbyname failed. err=%s", this, szOem, PszWSALastError())); return NM_CALLERR_NAME_RESOLUTION; }
return CheckHostEnt(pHostInfo); }
HRESULT CCallResolver::ResolveUlsName(LPCTSTR pcszAddr) { TCHAR szIP[MAX_PATH]; TCHAR szServer[MAX_PATH];
LPCTSTR pcsz = ExtractServerName(pcszAddr, szServer, CCHMAX(szServer)); if (pcsz == pcszAddr) return NM_CALLERR_NAME_RESOLUTION;
HRESULT hr = S_OK; if( SUCCEEDED( hr = CNmLDAP::ResolveUser( pcsz, szServer, szIP, CCHMAX( szIP ) ) ) ) { hr = ResolveIpName( szIP ); } return hr; }
HRESULT CCallResolver::ResolveIpName(LPCTSTR pcszAddr) { DWORD dwIP = inet_addr(pcszAddr); if (INADDR_NONE == dwIP) return NM_CALLERR_NAME_RESOLUTION;
char * pAddr = (char *) &dwIP; HOSTENT hostInfo; ClearStruct(&hostInfo); hostInfo.h_addrtype = AF_INET; hostInfo.h_length = sizeof(DWORD); hostInfo.h_addr_list = &pAddr;
return CheckHostEnt(&hostInfo); }
HRESULT CCallResolver::ResolveGateway(LPCTSTR pcszAddr) { TCHAR szGateway[MAX_PATH];
LPCTSTR pchSlash = _StrChr(pcszAddr, _T('/')); if (NULL == pchSlash) { WARNING_OUT(("CCallResolver: %08X ResolveGateway(%s) no separator?", this, pcszAddr)); return NM_CALLERR_NAME_RESOLUTION; }
lstrcpyn(szGateway, pcszAddr, (int)(1 + (pchSlash-pcszAddr))); return ResolveIpName(szGateway); }
/* R E S O L V E */ /*-------------------------------------------------------------------------
%%Function: Resolve
Attempt to resolve the string into a standard IP address. -------------------------------------------------------------------------*/ HRESULT CCallResolver::Resolve() { DBGENTRY(CCallResolver::Resolve);
HRESULT hr = E_FAIL;
switch (m_addrType) { case NM_ADDR_UNKNOWN: { if (NULL != _StrChr(m_pszAddr, _T('/'))) { if(SUCCEEDED(hr = ResolveUlsName(m_pszAddr))) { m_addrType = NM_ADDR_ULS; } break; }
if (FIpAddress(m_pszAddr)) { if(SUCCEEDED(hr = ResolveIpName(m_pszAddr))) { m_addrType = NM_ADDR_IP; } break; }
if(SUCCEEDED(hr = ResolveMachineName(m_pszAddr))) { m_addrType = NM_ADDR_MACHINENAME; } break; }
case NM_ADDR_H323_GATEWAY: { LPTSTR pch = (LPTSTR) _StrChr(m_pszAddr, _T('/')); if (NULL != pch) { // Address is in the format: Gateway/address
// e.g. "157.59.0.40/65000" or "efusion/65000"
*pch = _T('\0'); pch++; hr = ResolveIpName(m_pszAddr); if (FAILED(hr)) { hr = ResolveMachineName(m_pszAddr); if (FAILED(hr)) { break; } } LPTSTR pszNumber = PszAlloc(pch); delete m_pszAddr; m_pszAddr = pszNumber; } else { TCHAR sz[MAX_PATH]; if (0 == GetDefaultGateway(sz, CCHMAX(sz))) { hr = E_FAIL; break; } hr = ResolveIpName(sz); if (FAILED(hr)) { hr = ResolveMachineName(sz); if (FAILED(hr)) { break; } } } hr = FEmptySz(m_pszAddr) ? E_INVALIDARG : S_OK; break; }
case NM_ADDR_ULS: // Make sure the address is prefixed with an ILS server
if (NULL == _StrChr(m_pszAddr, _T('/'))) { TCHAR szAddr[CCHMAXSZ_ADDRESS]; if (!FCreateIlsName(szAddr, NULL, m_pszAddr, CCHMAX(szAddr))) { hr = E_FAIL; break; } delete m_pszAddr; m_pszAddr = PszAlloc(szAddr); }
hr = ResolveUlsName(m_pszAddr); break;
case NM_ADDR_IP: hr = ResolveIpName(m_pszAddr); if (FAILED(hr) && (hr != NM_CALLERR_LOOPBACK) ) { hr = ResolveMachineName(m_pszAddr); } break;
case NM_ADDR_MACHINENAME: hr = ResolveMachineName(m_pszAddr); break;
case NM_ADDR_ALIAS_ID: case NM_ADDR_ALIAS_E164: case NM_ADDR_T120_TRANSPORT: hr = FEmptySz(m_pszAddr) ? E_INVALIDARG : S_OK; break;
default: WARNING_OUT(("Resolve: Unsupported address type %d", m_addrType)); ASSERT(E_FAIL == hr); break; } /* switch (addrType) */
WARNING_OUT(("CCallResolver::Resolve(%d,%s) result=%08X", m_addrType, m_pszAddrIP ? m_pszAddrIP : m_pszAddr, hr));
DBGEXIT_HR(CCallResolver::Resolve, hr); return hr; }
|