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.
1161 lines
26 KiB
1161 lines
26 KiB
// File: h323cc.cpp
|
|
|
|
|
|
#include "precomp.h"
|
|
#include "confreg.h"
|
|
#include "version.h"
|
|
|
|
EXTERN_C HINSTANCE g_hInst=NULL; // global module instance
|
|
|
|
IRTP *g_pIRTP = NULL;
|
|
UINT g_capFlags = CAPFLAGS_AV_ALL;
|
|
|
|
#ifdef DEBUG
|
|
HDBGZONE ghDbgZoneCC = NULL;
|
|
static PTCHAR _rgZonesCC[] = {
|
|
TEXT("H323"),
|
|
TEXT("Init"),
|
|
TEXT("Conn"),
|
|
TEXT("Channels"),
|
|
TEXT("Caps"),
|
|
TEXT("Member"),
|
|
TEXT("unused"),
|
|
TEXT("unused"),
|
|
TEXT("Ref count"),
|
|
TEXT("unused"),
|
|
TEXT("Profile spew")
|
|
};
|
|
|
|
|
|
int WINAPI CCDbgPrintf(LPTSTR lpszFormat, ... )
|
|
{
|
|
va_list v1;
|
|
va_start(v1, lpszFormat);
|
|
DbgPrintf("H323CC", lpszFormat, v1);
|
|
va_end(v1);
|
|
return TRUE;
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
// The product ID fields are defined in the standard as an array of bytes. ASCII
|
|
// characters are used regardless of local character set.
|
|
// default Product ID and version ID strings
|
|
|
|
static char DefaultProductID[] = H323_PRODUCTNAME_STR;
|
|
static char DefaultProductVersion[] = H323_PRODUCTRELEASE_STR;
|
|
|
|
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL,
|
|
DWORD fdwReason,
|
|
LPVOID lpvReserved);
|
|
|
|
BOOL WINAPI DllEntryPoint(
|
|
HINSTANCE hinstDLL, // handle to DLL module
|
|
DWORD fdwReason, // reason for calling function
|
|
LPVOID lpvReserved // reserved
|
|
)
|
|
{
|
|
switch(fdwReason)
|
|
{
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
DBGINIT(&ghDbgZoneCC, _rgZonesCC);
|
|
|
|
DBG_INIT_MEMORY_TRACKING(hinstDLL);
|
|
|
|
DisableThreadLibraryCalls(hinstDLL);
|
|
g_hInst = hinstDLL;
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
DBG_CHECK_MEMORY_TRACKING(hinstDLL);
|
|
|
|
DBGDEINIT(&ghDbgZoneCC);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HRESULT WINAPI CreateH323CC(IH323CallControl ** ppCC, BOOL fForCalling, UINT capFlags)
|
|
{
|
|
if(!ppCC)
|
|
return H323CC_E_INVALID_PARAM;
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
*ppCC = new CH323CallControl(fForCalling, capFlags);
|
|
if(!(*ppCC))
|
|
return H323CC_E_CREATE_FAILURE;
|
|
|
|
return hrSuccess;
|
|
|
|
}
|
|
|
|
|
|
BOOL CH323CallControl::m_fGKProhibit = FALSE;
|
|
RASNOTIFYPROC CH323CallControl::m_pRasNotifyProc = NULL;
|
|
|
|
CH323CallControl::CH323CallControl(BOOL fForCalling, UINT capFlags) :
|
|
m_uRef(1),
|
|
m_fForCalling(fForCalling),
|
|
m_numlines(0),
|
|
m_pProcNotifyConnect(NULL),
|
|
m_pCapabilityResolver(NULL),
|
|
m_pListenLine(NULL),
|
|
m_pLineList(NULL),
|
|
m_pNextToAccept(NULL),
|
|
m_pUserName(NULL),
|
|
m_pLocalAliases(NULL),
|
|
m_pRegistrationAliases(NULL),
|
|
hrLast(hrSuccess),
|
|
m_pSendAudioChannel(NULL),
|
|
m_pSendVideoChannel(NULL),
|
|
m_uMaximumBandwidth(0)
|
|
{
|
|
//
|
|
// Set up caps.
|
|
//
|
|
if (fForCalling)
|
|
{
|
|
g_capFlags = capFlags;
|
|
}
|
|
|
|
m_VendorInfo.bCountryCode = USA_H221_COUNTRY_CODE;
|
|
m_VendorInfo.bExtension = USA_H221_COUNTRY_EXTENSION;
|
|
m_VendorInfo.wManufacturerCode = MICROSOFT_H_221_MFG_CODE;
|
|
|
|
m_VendorInfo.pProductNumber = (PCC_OCTETSTRING)MemAlloc(sizeof(CC_OCTETSTRING)
|
|
+ sizeof(DefaultProductID));
|
|
if(m_VendorInfo.pProductNumber)
|
|
{
|
|
m_VendorInfo.pProductNumber->wOctetStringLength = sizeof(DefaultProductID);
|
|
m_VendorInfo.pProductNumber->pOctetString =
|
|
((BYTE *)m_VendorInfo.pProductNumber + sizeof(CC_OCTETSTRING));
|
|
memcpy(m_VendorInfo.pProductNumber->pOctetString,
|
|
DefaultProductID, sizeof(DefaultProductID));
|
|
}
|
|
|
|
m_VendorInfo.pVersionNumber = (PCC_OCTETSTRING)MemAlloc(sizeof(CC_OCTETSTRING)
|
|
+ sizeof(DefaultProductVersion));
|
|
if(m_VendorInfo.pVersionNumber)
|
|
{
|
|
m_VendorInfo.pVersionNumber->wOctetStringLength = sizeof(DefaultProductVersion);
|
|
m_VendorInfo.pVersionNumber->pOctetString =
|
|
((BYTE *)m_VendorInfo.pVersionNumber + sizeof(CC_OCTETSTRING));
|
|
memcpy(m_VendorInfo.pVersionNumber->pOctetString,
|
|
DefaultProductVersion, sizeof(DefaultProductVersion));
|
|
}
|
|
|
|
RegEntry reCC(szRegInternetPhone TEXT("\\") szRegInternetPhoneNac,
|
|
HKEY_LOCAL_MACHINE,
|
|
FALSE,
|
|
KEY_READ);
|
|
|
|
UINT uAPD = reCC.GetNumberIniStyle(TEXT ("AudioPacketDurationMs"), 0);
|
|
if (uAPD)
|
|
{
|
|
g_AudioPacketDurationMs = uAPD;
|
|
g_fRegAudioPacketDuration = TRUE;
|
|
}
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
m_pCapabilityResolver = new CapsCtl();
|
|
if (!m_pCapabilityResolver)
|
|
{
|
|
ERRORMESSAGE(("CH323CallControl::CH323CallControl:cannot create capability resolver\r\n"));
|
|
hrLast = H323CC_E_INIT_FAILURE;
|
|
}
|
|
|
|
if(!m_pCapabilityResolver->Init())
|
|
{
|
|
ERRORMESSAGE(("CH323CallControl::CH323CallControl cannot init capability resolver\r\n"));
|
|
hrLast = H323CC_E_INIT_FAILURE;
|
|
}
|
|
|
|
}
|
|
|
|
HRESULT CH323CallControl::Initialize(PORT *lpPort)
|
|
{
|
|
FX_ENTRY("CH323CallControl::Initialize");
|
|
|
|
OBJ_CPT_RESET;
|
|
|
|
ASSERT(m_fForCalling);
|
|
|
|
if(!HR_SUCCEEDED(LastHR()))
|
|
{
|
|
goto EXIT;
|
|
}
|
|
if(!lpPort)
|
|
{
|
|
SetLastHR(H323CC_E_INVALID_PARAM);
|
|
goto EXIT;
|
|
}
|
|
|
|
if(!Init())
|
|
{
|
|
goto EXIT;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_pListenLine);
|
|
hrLast = m_pListenLine->GetLocalPort(lpPort);
|
|
}
|
|
|
|
if (g_capFlags & CAPFLAGS_AV_STREAMS)
|
|
{
|
|
SetLastHR( ::CoCreateInstance(CLSID_RTP,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IRTP,
|
|
(void**)&g_pIRTP) );
|
|
}
|
|
SHOW_OBJ_ETIME("CH323CallControl::Initialize");
|
|
|
|
EXIT:
|
|
return LastHR();
|
|
|
|
}
|
|
|
|
HRESULT CH323CallControl::SetMaxPPBandwidth(UINT Bandwidth)
|
|
{
|
|
HRESULT hr = hrSuccess;
|
|
LPAPPVIDCAPPIF lpIVidAppCap = NULL;
|
|
DWORD dwcFormats = 0;
|
|
DWORD dwcFormatsReturned = 0;
|
|
DWORD x;
|
|
BASIC_VIDCAP_INFO *pvidcaps = NULL;
|
|
|
|
m_uMaximumBandwidth =Bandwidth;
|
|
|
|
if (g_capFlags & CAPFLAGS_AV_STREAMS)
|
|
{
|
|
//Set the bandwidth on every video format
|
|
hr = QueryInterface(IID_IAppVidCap, (void **)&lpIVidAppCap);
|
|
if (! HR_SUCCEEDED (hr))
|
|
goto EXIT;
|
|
|
|
// Get the number of BASIC_VIDCAP_INFO structures available
|
|
hr = lpIVidAppCap->GetNumFormats((UINT*)&dwcFormats);
|
|
if (! HR_SUCCEEDED (hr))
|
|
goto EXIT;
|
|
|
|
if (dwcFormats > 0)
|
|
{
|
|
// Allocate some memory to hold the list in
|
|
if (!(pvidcaps = (BASIC_VIDCAP_INFO*)MemAlloc(dwcFormats * sizeof (BASIC_VIDCAP_INFO))))
|
|
{
|
|
hr = H323CC_E_INSUFFICIENT_MEMORY;
|
|
goto EXIT;
|
|
}
|
|
// Get the list
|
|
hr=lpIVidAppCap->EnumFormats(pvidcaps, dwcFormats * sizeof (BASIC_VIDCAP_INFO),
|
|
(UINT*)&dwcFormatsReturned);
|
|
if (! HR_SUCCEEDED (hr))
|
|
goto EXIT;
|
|
|
|
//Set the bandwidth on each format
|
|
for (x=0;x<dwcFormatsReturned;x++)
|
|
{
|
|
pvidcaps[x].uMaxBitrate=m_uMaximumBandwidth;
|
|
}
|
|
|
|
// Ok, now submit this list
|
|
hr = lpIVidAppCap->ApplyAppFormatPrefs(pvidcaps, dwcFormats);
|
|
if (! HR_SUCCEEDED (hr))
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
//Initialize the default H.323 simcaps.
|
|
hr = m_pCapabilityResolver->ComputeCapabilitySets(m_uMaximumBandwidth);
|
|
//if(!HR_SUCCEEDED(hr))
|
|
// goto EXIT;
|
|
|
|
EXIT:
|
|
// let the interface go
|
|
if (lpIVidAppCap)
|
|
{
|
|
lpIVidAppCap->Release();
|
|
// (going out of scope) lpIVidAppCap = NULL;
|
|
}
|
|
if(pvidcaps)
|
|
{
|
|
// Free the memory, we're done
|
|
MemFree(pvidcaps);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
BOOL CH323CallControl::Init()
|
|
{
|
|
HRESULT hResult;
|
|
|
|
DEBUGMSG(ZONE_INIT,("Init: this:0x%08lX\r\n", this));
|
|
SetLastHR(hrSuccess);
|
|
|
|
if (m_fForCalling)
|
|
{
|
|
//
|
|
// Only call control code should init CC_ stuff. Codec manipulation
|
|
// via audiocpl should not.
|
|
//
|
|
hResult = CC_Initialize();
|
|
if(!HR_SUCCEEDED(hResult))
|
|
{
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
ASSERT(m_pCapabilityResolver);
|
|
|
|
// Initialize capability data using default number, but clear the saved
|
|
// bandwidth number afterwards. This detects attempts to place or
|
|
// accept calls before the application initializes the real bandwidth
|
|
hResult = SetMaxPPBandwidth(DEF_AP_BWMAX);
|
|
m_uMaximumBandwidth = 0;
|
|
if(!HR_SUCCEEDED(hResult))
|
|
{
|
|
goto CLEANUP;
|
|
}
|
|
|
|
// Create dual connection objects for listening for new connections
|
|
if (m_fForCalling)
|
|
{
|
|
hResult = CreateConnection(&m_pListenLine,PID_H323);
|
|
if(!HR_SUCCEEDED(hResult))
|
|
{
|
|
goto CLEANUP;
|
|
}
|
|
if(!m_pListenLine)
|
|
{
|
|
hResult = H323CC_E_INIT_FAILURE;
|
|
goto CLEANUP;
|
|
}
|
|
if(!(m_pListenLine->ListenOn(H323_PORT)))
|
|
{
|
|
hResult = H323CC_E_NETWORK_ERROR;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
CLEANUP:
|
|
if (m_pListenLine)
|
|
{
|
|
m_pListenLine->Release();
|
|
m_pListenLine = NULL;
|
|
}
|
|
SetLastHR(hResult);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
CH323CallControl::~CH323CallControl()
|
|
{
|
|
if(m_VendorInfo.pProductNumber)
|
|
MemFree(m_VendorInfo.pProductNumber);
|
|
if(m_VendorInfo.pVersionNumber)
|
|
MemFree(m_VendorInfo.pVersionNumber);
|
|
if(m_pUserName)
|
|
MemFree(m_pUserName);
|
|
if(m_pLocalAliases)
|
|
FreeTranslatedAliasList(m_pLocalAliases);
|
|
if(m_pRegistrationAliases)
|
|
FreeTranslatedAliasList(m_pRegistrationAliases);
|
|
|
|
if (m_pCapabilityResolver)
|
|
{
|
|
m_pCapabilityResolver->Release();
|
|
m_pCapabilityResolver = NULL;
|
|
}
|
|
|
|
if (m_pSendAudioChannel)
|
|
{
|
|
ASSERT(g_capFlags & CAPFLAGS_AV_STREAMS);
|
|
m_pSendAudioChannel->Release();
|
|
m_pSendAudioChannel = NULL;
|
|
}
|
|
|
|
if (m_pSendVideoChannel)
|
|
{
|
|
ASSERT(g_capFlags & CAPFLAGS_AV_STREAMS);
|
|
m_pSendVideoChannel->Release();
|
|
m_pSendVideoChannel = NULL;
|
|
}
|
|
|
|
if (m_fForCalling)
|
|
{
|
|
// toast backward references to this in all
|
|
// connection objects
|
|
CConnection *pLine = m_pLineList;
|
|
CConnection *pNext;
|
|
while(pLine)
|
|
{
|
|
pNext = pLine->next;
|
|
pLine->DeInit();
|
|
pLine = pNext;
|
|
}
|
|
|
|
// release the listening object if it exists
|
|
if(m_pListenLine)
|
|
m_pListenLine->Release();
|
|
|
|
// shutdown CALLCONT.DLL
|
|
CC_Shutdown();
|
|
|
|
if (g_pIRTP)
|
|
{
|
|
ASSERT(g_capFlags & CAPFLAGS_AV_STREAMS);
|
|
g_pIRTP->Release();
|
|
g_pIRTP = NULL;
|
|
}
|
|
|
|
// Put capflags back
|
|
g_capFlags = CAPFLAGS_AV_ALL;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!m_pLineList);
|
|
ASSERT(!m_pListenLine);
|
|
}
|
|
}
|
|
|
|
ULONG CH323CallControl::AddRef()
|
|
{
|
|
m_uRef++;
|
|
return m_uRef;
|
|
}
|
|
|
|
ULONG CH323CallControl::Release()
|
|
{
|
|
m_uRef--;
|
|
if(m_uRef == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_uRef;
|
|
}
|
|
|
|
HRESULT CH323CallControl::SetUserDisplayName(LPWSTR lpwName)
|
|
{
|
|
LPWSTR lpwD;
|
|
ULONG ulSize;
|
|
if(!lpwName)
|
|
{
|
|
return (MakeResult(H323CC_E_INVALID_PARAM));
|
|
}
|
|
if(lpwName)
|
|
{
|
|
ulSize = ((lstrlenW(lpwName) +1)*sizeof(WCHAR));
|
|
lpwD = (LPWSTR)MemAlloc(ulSize);
|
|
if(!lpwD)
|
|
return H323CC_E_INSUFFICIENT_MEMORY;
|
|
|
|
if(m_pUserName)
|
|
{
|
|
MemFree(m_pUserName);
|
|
}
|
|
|
|
m_pUserName = lpwD;
|
|
memcpy(m_pUserName, lpwName, ulSize);
|
|
}
|
|
return (MakeResult(hrSuccess));
|
|
}
|
|
|
|
// Find the most suitable alias for display. Return the first H323ID if it exists,
|
|
// else return the first E.164 address
|
|
PCC_ALIASITEM CH323CallControl::GetUserDisplayAlias()
|
|
{
|
|
WORD wC;
|
|
PCC_ALIASITEM pItem, pFoundItem = NULL;
|
|
if(m_pLocalAliases)
|
|
{
|
|
wC = m_pLocalAliases->wCount;
|
|
pItem = m_pLocalAliases->pItems;
|
|
while (wC--)
|
|
{
|
|
if(!pItem)
|
|
{
|
|
continue;
|
|
}
|
|
if(pItem->wType == CC_ALIAS_H323_ID)
|
|
{
|
|
if(!pItem->wDataLength || !pItem->pData)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
pFoundItem = pItem; // done, done, done
|
|
break; // I said done
|
|
}
|
|
}
|
|
else if(pItem->wType == CC_ALIAS_H323_PHONE)
|
|
{
|
|
if(!pItem->wDataLength || !pItem->pData)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if(!pFoundItem) // if nothing at all was found so far
|
|
pFoundItem = pItem; // remember this
|
|
}
|
|
}
|
|
pItem++;
|
|
}
|
|
}
|
|
return pFoundItem;
|
|
}
|
|
|
|
CREQ_RESPONSETYPE CH323CallControl::ConnectionRequest(CConnection *pConnection)
|
|
{
|
|
CREQ_RESPONSETYPE Response;
|
|
// decide what to do internally
|
|
// LOOKLOOK hardcoded acceptance
|
|
Response = CRR_ACCEPT;
|
|
return Response;
|
|
}
|
|
CREQ_RESPONSETYPE CH323CallControl::FilterConnectionRequest(CConnection *pConnection,
|
|
P_APP_CALL_SETUP_DATA pAppData)
|
|
{
|
|
CREQ_RESPONSETYPE Response = CRR_ASYNC;
|
|
ASSERT(m_uMaximumBandwidth);
|
|
// run it past the notification callback (if there is one)
|
|
if(m_pProcNotifyConnect)
|
|
{
|
|
// pass ptr to IConnection
|
|
Response = (m_pProcNotifyConnect)((IH323Endpoint *)&pConnection->m_ImpConnection,
|
|
pAppData);
|
|
if(Response != CRR_ACCEPT)
|
|
{
|
|
return Response;
|
|
}
|
|
}
|
|
return Response;
|
|
}
|
|
|
|
|
|
HRESULT CH323CallControl::RegisterConnectionNotify(CNOTIFYPROC pConnectRequestHandler)
|
|
{
|
|
|
|
// reject if there's an existing registration
|
|
if (m_pProcNotifyConnect || (!pConnectRequestHandler))
|
|
{
|
|
return H323CC_E_INVALID_PARAM;
|
|
}
|
|
m_pProcNotifyConnect = pConnectRequestHandler;
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CH323CallControl::DeregisterConnectionNotify(CNOTIFYPROC pConnectRequestHandler)
|
|
{
|
|
// reject if there's not an existing registration
|
|
if (!m_pProcNotifyConnect)
|
|
return H323CC_E_INVALID_PARAM;
|
|
if (pConnectRequestHandler == m_pProcNotifyConnect)
|
|
{
|
|
m_pProcNotifyConnect = NULL;
|
|
}
|
|
else
|
|
{
|
|
return H323CC_E_INVALID_PARAM;
|
|
}
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CH323CallControl::GetNumConnections(ULONG *lp)
|
|
{
|
|
ULONG ulRet = m_numlines;
|
|
// hide the "listening" connection object from the client/ui/whatever
|
|
if(ulRet && m_pListenLine)
|
|
ulRet--;
|
|
if(lp)
|
|
{
|
|
*lp = ulRet;
|
|
}
|
|
return hrSuccess;
|
|
}
|
|
|
|
HRESULT CH323CallControl::GetConnobjArray(CConnection **lplpArray, UINT uSize)
|
|
{
|
|
UINT uPublicConnections; // # of visible objects
|
|
if(!lplpArray)
|
|
return H323CC_E_INVALID_PARAM;
|
|
|
|
uPublicConnections = m_numlines;
|
|
if(m_pListenLine)
|
|
uPublicConnections--;
|
|
|
|
if(uSize < (sizeof(CConnection **) * uPublicConnections))
|
|
{
|
|
return H323CC_E_MORE_CONNECTIONS;
|
|
}
|
|
|
|
CConnection *pLine = m_pLineList;
|
|
CConnection *pNext;
|
|
int i=0;
|
|
while(pLine)
|
|
{
|
|
DEBUGCHK(uSize--);
|
|
pNext = pLine->next;
|
|
// return everything but the objects used for listening
|
|
if(pLine != m_pListenLine)
|
|
{
|
|
lplpArray[i++] = pLine;
|
|
}
|
|
pLine = pNext;
|
|
}
|
|
|
|
return hrSuccess;
|
|
};
|
|
|
|
|
|
|
|
HRESULT CH323CallControl::GetConnectionArray(IH323Endpoint * *lplpArray, UINT uSize)
|
|
{
|
|
|
|
UINT uPublicConnections; // # of visible objects
|
|
if(!lplpArray)
|
|
return H323CC_E_INVALID_PARAM;
|
|
|
|
uPublicConnections = m_numlines;
|
|
if(m_pListenLine)
|
|
uPublicConnections--;
|
|
|
|
if(uSize < (sizeof(IH323Endpoint * *) * uPublicConnections))
|
|
{
|
|
return H323CC_E_MORE_CONNECTIONS;
|
|
}
|
|
|
|
CConnection *pLine = m_pLineList;
|
|
CConnection *pNext;
|
|
int i=0;
|
|
while(pLine)
|
|
{
|
|
DEBUGCHK(uSize--);
|
|
pNext = pLine->next;
|
|
// return everything but the objects used for listening
|
|
if(pLine != m_pListenLine)
|
|
{
|
|
lplpArray[i++] = (IH323Endpoint *)&pLine->m_ImpConnection;
|
|
}
|
|
pLine = pNext;
|
|
}
|
|
|
|
return hrSuccess;
|
|
};
|
|
|
|
//
|
|
// protocol specific CreateConnection
|
|
//
|
|
HRESULT CH323CallControl::CreateConnection(CConnection **lplpConnection, GUID PIDofProtocolType)
|
|
{
|
|
SetLastHR(hrSuccess);
|
|
CConnection *lpConnection, *lpList;
|
|
if(!lplpConnection)
|
|
{
|
|
SetLastHR(MakeResult(H323CC_E_INVALID_PARAM));
|
|
goto EXIT;
|
|
}
|
|
|
|
*lplpConnection = NULL;
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
if(!(lpConnection = new CConnection))
|
|
{
|
|
SetLastHR(MakeResult(H323CC_E_INSUFFICIENT_MEMORY));
|
|
goto EXIT;
|
|
}
|
|
|
|
hrLast = lpConnection->Init(this, PIDofProtocolType);
|
|
|
|
// LOOKLOOK need to insert this connection in the connection list
|
|
if(!HR_SUCCEEDED(hrSuccess))
|
|
{
|
|
delete lpConnection;
|
|
lpConnection = NULL;
|
|
}
|
|
else
|
|
{
|
|
*lplpConnection = lpConnection;
|
|
// insert in connection list
|
|
lpList = m_pLineList;
|
|
m_pLineList = lpConnection;
|
|
lpConnection->next =lpList;
|
|
m_numlines++;
|
|
}
|
|
EXIT:
|
|
return (LastHR());
|
|
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// IH323CallControl->CreateConnection(), EXTERNAL create connection interface.
|
|
//
|
|
HRESULT CH323CallControl::CreateConnection(IH323Endpoint * *lplpLine, GUID PIDofProtocolType)
|
|
{
|
|
SetLastHR(hrSuccess);
|
|
CConnection *lpConnection;
|
|
ASSERT(m_uMaximumBandwidth);
|
|
if(!m_uMaximumBandwidth)
|
|
{
|
|
SetLastHR(MakeResult(H323CC_E_NOT_INITIALIZED));
|
|
goto EXIT;
|
|
}
|
|
if(!lplpLine)
|
|
{
|
|
SetLastHR(MakeResult(H323CC_E_INVALID_PARAM));
|
|
goto EXIT;
|
|
}
|
|
*lplpLine = NULL;
|
|
|
|
hrLast = CreateConnection(&lpConnection, PIDofProtocolType);
|
|
|
|
if(HR_SUCCEEDED(LastHR()) && lpConnection)
|
|
{
|
|
*lplpLine = (IH323Endpoint *)&lpConnection->m_ImpConnection;
|
|
}
|
|
EXIT:
|
|
return (LastHR());
|
|
}
|
|
|
|
//
|
|
// CreateLocalCommChannel creates the send side of a media channel outside the context
|
|
// of any call.
|
|
//
|
|
|
|
HRESULT CH323CallControl::CreateLocalCommChannel(ICommChannel** ppCommChan, LPGUID lpMID,
|
|
IMediaChannel* pMediaStream)
|
|
{
|
|
|
|
if(!ppCommChan || !lpMID || !pMediaStream)
|
|
return H323CC_E_INVALID_PARAM;
|
|
|
|
if (*lpMID == MEDIA_TYPE_H323AUDIO)
|
|
{
|
|
ASSERT(g_capFlags & CAPFLAGS_AV_STREAMS);
|
|
|
|
// allow only one of each media type to be created. This is an artificial
|
|
// limitation.
|
|
if(m_pSendAudioChannel)
|
|
{
|
|
hrLast = H323CC_E_CREATE_FAILURE;
|
|
goto EXIT;
|
|
}
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
if(!(m_pSendAudioChannel = new ImpICommChan))
|
|
{
|
|
hrLast = H323CC_E_CREATE_FAILURE;
|
|
goto EXIT;
|
|
}
|
|
|
|
hrLast = m_pSendAudioChannel->StandbyInit(lpMID, m_pCapabilityResolver,
|
|
pMediaStream);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
m_pSendAudioChannel->Release();
|
|
m_pSendAudioChannel = NULL;
|
|
goto EXIT;
|
|
}
|
|
|
|
hrLast = m_pSendAudioChannel->QueryInterface(IID_ICommChannel, (void **)ppCommChan);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
m_pSendAudioChannel->Release();
|
|
m_pSendAudioChannel = NULL;
|
|
goto EXIT;
|
|
}
|
|
}
|
|
else if (*lpMID == MEDIA_TYPE_H323VIDEO)
|
|
{
|
|
ASSERT(g_capFlags & CAPFLAGS_AV_STREAMS);
|
|
|
|
// allow only one of each media type to be created. This is an artificial
|
|
// limitation.
|
|
if(m_pSendVideoChannel)
|
|
{
|
|
hrLast = H323CC_E_CREATE_FAILURE;
|
|
goto EXIT;
|
|
}
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
if(!(m_pSendVideoChannel = new ImpICommChan))
|
|
{
|
|
hrLast = H323CC_E_CREATE_FAILURE;
|
|
goto EXIT;
|
|
}
|
|
hrLast = m_pSendVideoChannel->StandbyInit(lpMID, m_pCapabilityResolver,
|
|
pMediaStream);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
m_pSendVideoChannel->Release();
|
|
m_pSendVideoChannel = NULL;
|
|
goto EXIT;
|
|
}
|
|
hrLast = m_pSendVideoChannel->QueryInterface(IID_ICommChannel, (void **)ppCommChan);
|
|
if(!HR_SUCCEEDED(hrLast))
|
|
{
|
|
m_pSendVideoChannel->Release();
|
|
m_pSendVideoChannel = NULL;
|
|
goto EXIT;
|
|
}
|
|
}
|
|
else
|
|
hrLast = H323CC_E_INVALID_PARAM;
|
|
EXIT:
|
|
return hrLast;
|
|
}
|
|
|
|
|
|
ICtrlCommChan *CH323CallControl::QueryPreviewChannel(LPGUID lpMID)
|
|
{
|
|
HRESULT hr;
|
|
ICtrlCommChan *pCommChan = NULL;
|
|
if(*lpMID == MEDIA_TYPE_H323AUDIO)
|
|
{
|
|
if(m_pSendAudioChannel)
|
|
{
|
|
hr = m_pSendAudioChannel->QueryInterface(IID_ICtrlCommChannel, (void **)&pCommChan);
|
|
if(HR_SUCCEEDED(hr))
|
|
{
|
|
return pCommChan;
|
|
}
|
|
}
|
|
}
|
|
else if (*lpMID == MEDIA_TYPE_H323VIDEO)
|
|
{
|
|
if(m_pSendVideoChannel)
|
|
{
|
|
hr = m_pSendVideoChannel->QueryInterface(IID_ICtrlCommChannel, (void **)&pCommChan);
|
|
if(HR_SUCCEEDED(hr))
|
|
{
|
|
return pCommChan;
|
|
}
|
|
}
|
|
}
|
|
// fallout to error case
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HRESULT CH323CallControl::RemoveConnection(CConnection *lpConnection)
|
|
{
|
|
SetLastHR(hrSuccess);
|
|
CConnection *lpList;
|
|
UINT nLines;
|
|
|
|
|
|
if((lpConnection == NULL) || lpConnection->m_pH323CallControl != this)
|
|
{
|
|
SetLastHR(MakeResult(H323CC_E_INVALID_PARAM));
|
|
goto EXIT;
|
|
}
|
|
|
|
m_numlines--; // update count NOW
|
|
|
|
|
|
// use # of lines for bug detection in list management code
|
|
nLines = m_numlines;
|
|
|
|
|
|
if(m_pListenLine == lpConnection)
|
|
m_pListenLine = NULL;
|
|
|
|
// zap the back pointer of the connection NOW - this is crucial for
|
|
// implementing "asynchronous delete" of connection objects
|
|
lpConnection->m_pH323CallControl = NULL;
|
|
|
|
// find it in the connection list and remove it
|
|
|
|
// sp. case head
|
|
if(m_pLineList== lpConnection)
|
|
{
|
|
m_pLineList = lpConnection->next;
|
|
}
|
|
else
|
|
{
|
|
lpList = m_pLineList;
|
|
while(lpList->next && nLines)
|
|
{
|
|
if(lpList->next == lpConnection)
|
|
{
|
|
lpList->next = lpConnection->next;
|
|
break;
|
|
}
|
|
lpList = lpList->next;
|
|
nLines--;
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
return (LastHR());
|
|
}
|
|
|
|
|
|
STDMETHODIMP CH323CallControl::QueryInterface( REFIID iid, void ** ppvObject)
|
|
{
|
|
// this breaks the rules for the official COM QueryInterface because
|
|
// the interfaces that are queried for are not necessarily real COM
|
|
// interfaces. The reflexive property of QueryInterface would be broken in
|
|
// that case.
|
|
HRESULT hr = E_NOINTERFACE;
|
|
if(!ppvObject)
|
|
return hr;
|
|
|
|
*ppvObject = 0;
|
|
if ((iid == IID_IH323CC) || (iid == IID_IUnknown))// satisfy symmetric property of QI
|
|
{
|
|
*ppvObject = this;
|
|
hr = hrSuccess;
|
|
AddRef();
|
|
}
|
|
else if (iid == IID_IAppAudioCap )
|
|
{
|
|
hr = m_pCapabilityResolver->QueryInterface(iid, ppvObject);
|
|
}
|
|
else if(iid == IID_IAppVidCap )
|
|
{
|
|
hr = m_pCapabilityResolver->QueryInterface(iid, ppvObject);
|
|
}
|
|
else if(iid == IID_IDualPubCap )
|
|
{
|
|
hr = m_pCapabilityResolver->QueryInterface(iid, ppvObject);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a copy of the alias names in the (somewhat bogus) format that
|
|
// CALLCONT expects. The destination format has a two-part string for every
|
|
// entry, but the lower layers concatenate the parts. Someday H323CC and CALLCONT
|
|
// will be one, and all the extraneous layers, copies of data, and redundant
|
|
// validations won't be needed.
|
|
//
|
|
|
|
HRESULT AllocTranslatedAliasList(PCC_ALIASNAMES *ppDest, P_H323ALIASLIST pSource)
|
|
{
|
|
HRESULT hr = H323CC_E_INVALID_PARAM;
|
|
WORD w;
|
|
PCC_ALIASNAMES pNewAliases = NULL;
|
|
PCC_ALIASITEM pDestItem;
|
|
P_H323ALIASNAME pSrcItem;
|
|
|
|
if(!ppDest || !pSource || pSource->wCount == 0)
|
|
{
|
|
goto ERROR_OUT;
|
|
}
|
|
*ppDest = NULL;
|
|
pNewAliases = (PCC_ALIASNAMES)MemAlloc(sizeof(CC_ALIASNAMES));
|
|
if(!pNewAliases)
|
|
{
|
|
hr = H323CC_E_INSUFFICIENT_MEMORY;
|
|
goto ERROR_OUT;
|
|
}
|
|
pNewAliases->wCount = 0;
|
|
pNewAliases->pItems = (PCC_ALIASITEM)MemAlloc(pSource->wCount*sizeof(CC_ALIASITEM));
|
|
if(!pNewAliases->pItems)
|
|
{
|
|
hr = H323CC_E_INSUFFICIENT_MEMORY;
|
|
goto ERROR_OUT;
|
|
}
|
|
for(w=0;w<pSource->wCount;w++)
|
|
{
|
|
pDestItem = pNewAliases->pItems+w;
|
|
pSrcItem = pSource->pItems+w;
|
|
// don't tolerate empty entries - error out if any exist
|
|
if(pSrcItem->wDataLength && pSrcItem->lpwData)
|
|
{
|
|
if(pSrcItem->aType ==AT_H323_ID)
|
|
{
|
|
pDestItem->wType = CC_ALIAS_H323_ID;
|
|
}
|
|
else if(pSrcItem->aType ==AT_H323_E164)
|
|
{
|
|
pDestItem->wType = CC_ALIAS_H323_PHONE;
|
|
}
|
|
else
|
|
{ // don't know how to translate this. I hope that the need for translation
|
|
// goes away before new alias types are added. Adding an alias type
|
|
// (H323_URL for example) requires many changes in lower layers anyway,
|
|
// so that would be a good time to merge H323CC and CALLCONT.
|
|
goto ERROR_OUT; // return invalid param
|
|
}
|
|
pDestItem->wPrefixLength = 0; // this prefix thing is bogus
|
|
pDestItem->pPrefix = NULL;
|
|
pDestItem->pData = (LPWSTR)MemAlloc(pSrcItem->wDataLength *sizeof(WCHAR));
|
|
if(pDestItem->pData == NULL)
|
|
{
|
|
hr = H323CC_E_INSUFFICIENT_MEMORY;
|
|
goto ERROR_OUT;
|
|
}
|
|
// got good data. Copy the data, set size/length, and count it
|
|
memcpy(pDestItem->pData, pSrcItem->lpwData, pSrcItem->wDataLength * sizeof(WCHAR));
|
|
pDestItem->wDataLength = pSrcItem->wDataLength;
|
|
pNewAliases->wCount++;
|
|
}
|
|
else
|
|
{
|
|
goto ERROR_OUT;
|
|
}
|
|
}
|
|
// got here, so output good data
|
|
hr = hrSuccess;
|
|
*ppDest = pNewAliases;
|
|
//pNewAliases = NULL; // not needed if returning here instead of falling out
|
|
return hr;
|
|
|
|
ERROR_OUT:
|
|
if(pNewAliases) // then it's an error condition needing cleanup
|
|
{
|
|
FreeTranslatedAliasList(pNewAliases);
|
|
}
|
|
return hr;
|
|
}
|
|
VOID FreeTranslatedAliasList(PCC_ALIASNAMES pDoomed)
|
|
{
|
|
WORD w;
|
|
PCC_ALIASITEM pDoomedItem;
|
|
if(!pDoomed)
|
|
return;
|
|
|
|
for(w=0;w<pDoomed->wCount;w++)
|
|
{
|
|
pDoomedItem = pDoomed->pItems+w;
|
|
|
|
// don't tolerate empty entries - error out if any exist
|
|
if(pDoomedItem->wDataLength && pDoomedItem->pData)
|
|
{
|
|
MemFree(pDoomedItem->pData);
|
|
}
|
|
else
|
|
ASSERT(0);
|
|
}
|
|
MemFree(pDoomed->pItems);
|
|
MemFree(pDoomed);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CH323CallControl::SetUserAliasNames(P_H323ALIASLIST pAliases)
|
|
{
|
|
HRESULT hr = hrSuccess;
|
|
PCC_ALIASNAMES pNewAliases = NULL;
|
|
PCC_ALIASITEM pItem;
|
|
|
|
hr = AllocTranslatedAliasList(&pNewAliases, pAliases);
|
|
if(!HR_SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
ASSERT(pNewAliases);
|
|
if(m_pLocalAliases)
|
|
FreeTranslatedAliasList(m_pLocalAliases);
|
|
|
|
m_pLocalAliases = pNewAliases;
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CH323CallControl::EnableGatekeeper(BOOL bEnable,
|
|
PSOCKADDR_IN pGKAddr, P_H323ALIASLIST pAliases,
|
|
RASNOTIFYPROC pRasNotifyProc)
|
|
{
|
|
HRESULT hr = hrSuccess;
|
|
|
|
PCC_ALIASNAMES pNewAliases = NULL;
|
|
PCC_ALIASITEM pItem;
|
|
|
|
m_pRasNotifyProc = pRasNotifyProc;
|
|
if(bEnable)
|
|
{
|
|
if(!pRasNotifyProc || !pGKAddr || !pAliases)
|
|
{
|
|
return H323CC_E_INVALID_PARAM;
|
|
}
|
|
if((pGKAddr->sin_addr.s_addr == INADDR_NONE)
|
|
|| (pGKAddr->sin_addr.s_addr == INADDR_ANY))
|
|
{
|
|
return H323CC_E_INVALID_PARAM;
|
|
}
|
|
hr = AllocTranslatedAliasList(&pNewAliases, pAliases);
|
|
if(!HR_SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
ASSERT(pNewAliases);
|
|
if(m_pRegistrationAliases)
|
|
FreeTranslatedAliasList(m_pRegistrationAliases);
|
|
|
|
m_pRegistrationAliases = pNewAliases;
|
|
// reset "I can place calls" state
|
|
m_fGKProhibit = FALSE;
|
|
hr = CC_EnableGKRegistration(bEnable,
|
|
pGKAddr, m_pRegistrationAliases,
|
|
&m_VendorInfo,
|
|
0, // no multipoint/MC funtionality
|
|
RasNotify);
|
|
}
|
|
else
|
|
{
|
|
// we are turning off knowledge of what a gatekeeper is,
|
|
// so reset "I can place calls" state.
|
|
m_fGKProhibit = FALSE;
|
|
hr = CC_EnableGKRegistration(bEnable,
|
|
NULL, NULL, NULL, 0, RasNotify);
|
|
if(m_pRegistrationAliases)
|
|
FreeTranslatedAliasList(m_pRegistrationAliases);
|
|
|
|
m_pRegistrationAliases = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CH323CallControl::GetGKCallPermission()
|
|
{
|
|
if(m_fGKProhibit)
|
|
return CONN_E_GK_NOT_REGISTERED;
|
|
else
|
|
return hrSuccess;
|
|
}
|
|
|
|
VOID CALLBACK CH323CallControl::RasNotify(DWORD dwRasEvent, HRESULT hReason)
|
|
{
|
|
|
|
switch(dwRasEvent)
|
|
{
|
|
case RAS_REG_CONFIRM: // received RCF (registration confirmed)
|
|
// reset "I can place calls" state
|
|
m_fGKProhibit = FALSE;
|
|
break;
|
|
|
|
case RAS_REG_TIMEOUT: // GK did not respond
|
|
case RAS_UNREG_CONFIRM: // received UCF (unregistration confirmed)
|
|
default:
|
|
// do nothing. (except pass notification upward
|
|
break;
|
|
|
|
case RAS_REJECTED: // received RRJ (registration rejected)
|
|
m_fGKProhibit = TRUE;
|
|
break;
|
|
case RAS_UNREG_REQ: // received URQ
|
|
m_fGKProhibit = TRUE;
|
|
break;
|
|
}
|
|
if(m_pRasNotifyProc)
|
|
{
|
|
(m_pRasNotifyProc)(dwRasEvent, hReason);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|