/*
 *  	File: ctrlh323.cpp
 *
 *		Implementation of IControlChannel using H.323 call control protocol
 * 		via apis of CALLCONT.DLL
 *		
 *
 *		Revision History:
 *
 *		09/06/96	mikev	created
 *					
 */

#include "precomp.h"
#include "ctrlh323.h"
#include "version.h"
#include "strutil.h"

#ifdef DEBUG
VOID DumpChannelParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2);
VOID DumpNonstdParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2);
#else
#define DumpNonstdParameters(a, b)
#define DumpChannelParameters(a, b)
#endif

extern HRESULT AllocTranslatedAliasList(PCC_ALIASNAMES *ppDest, P_H323ALIASLIST pSource);
extern VOID FreeTranslatedAliasList(PCC_ALIASNAMES pDoomed);

static char DefaultProductID[] = H323_PRODUCTNAME_STR;
static char DefaultProductVersion[] = H323_PRODUCTRELEASE_STR;

HRESULT  CCConferenceCallback (BYTE						bIndication,
										HRESULT						hStatus,
										CC_HCONFERENCE                 hConference,
										DWORD_PTR                   dwConferenceToken,
										PCC_CONFERENCE_CALLBACK_PARAMS pConferenceCallbackParams);


VOID  CCListenCallback (HRESULT hStatus,PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams);

VOID CH323Ctrl::DoAdvise(DWORD dwEvent, LPVOID lpvData)
{
	FX_ENTRY ("CH323Ctrl::DoAdvise");

	if(IsReleasing())	// don't call out while releasing because it could call
						// back in!
	{
		ERRORMESSAGE(("%s:in releasing state\r\n",_fx_));
		return;
	}

	AddRef();	// protect ourselves from reentrant calls to Release().
	if(m_pConfAdvise)
	{
		hrLast = m_pConfAdvise->OnControlEvent(dwEvent, lpvData, this);
	}
	else
	{
		ERRORMESSAGE(("%s:Invalid m_pConfAdvise\r\n",_fx_));
	}
	
	Release();
}

VOID CH323Ctrl::GoNextPhase(CtlChanStateType phase)
{
	FX_ENTRY ("CH323Ctrl::GoNextPhase");
	BOOL fNotifyReady = FALSE;
	#define InvError() ERRORMESSAGE(("%s:Invalid transition from %d to %d\r\n",_fx_,m_Phase,phase))
	switch(phase)
	{
		case CCS_Idle:
			if(m_Phase != CCS_Idle && m_Phase != CCS_Disconnecting && m_Phase != CCS_Listening)
			{
				InvError();
			}
			else
			{
				m_ChanFlags &= ~(CTRLF_OPEN);
			}
		break;
		case CCS_Connecting:
			if((m_Phase != CCS_Idle) && (m_Phase != CCS_Ringing))
			{
				InvError();
			}
		break;
		case CCS_Accepting:
			if(m_Phase != CCS_Listening)
			{
				InvError();
			}
		
		break;
		case CCS_Ringing:
			// transition from CCS_Idle state is actually only valid if
			// there is an incoming call
			if(m_Phase != CCS_Connecting && m_Phase != CCS_Filtering && m_Phase != CCS_Listening)
			{
				InvError();
			}
		break;
		case CCS_Opening:
			if(m_Phase != CCS_Connecting && m_Phase != CCS_Accepting
				&& m_Phase != CCS_Ringing)
			{
				InvError();
			}
		break;
		case CCS_Closing:
			if(m_Phase != CCS_Opening && m_Phase != CCS_Ready && m_Phase != CCS_InUse)
			{
				InvError();
			}
		break;
		case CCS_Ready:
			// can be reentered. if notification is already pending, (state is
			// already CCS_InUse) stay there,  else do the transition
			if(m_Phase != CCS_InUse)
			{
				if(m_Phase != CCS_Opening)
				{
					InvError();
				}
				else
				{
					//signal "all channels ready" to IConfAdvise	
					fNotifyReady = TRUE;
				}
			}
			phase = CCS_InUse;
		break;
		case CCS_InUse:
			// previous state must be CCS_InUse or CCS_Ready
			if(m_Phase != CCS_InUse && m_Phase != CCS_Ready)
			{
				InvError();
			}
		
		break;
		case CCS_Listening:
			if(m_Phase != CCS_Idle)
			{
				InvError();
			}
		break;
		case CCS_Disconnecting:
			//if(m_Phase != CCS_Closing)
			//{
			//	InvError();
			//}
		break;

	}

	m_Phase = phase;

	if (fNotifyReady)
	{
		DoAdvise(CCEV_ALL_CHANNELS_READY, NULL);
	}
}


HRESULT CCConferenceCallback (BYTE bIndication,
	HRESULT	hConfStatus, CC_HCONFERENCE hConference, DWORD_PTR dwConferenceToken,
	PCC_CONFERENCE_CALLBACK_PARAMS pConferenceCallbackParams)
{
	HRESULT hr = CC_NOT_IMPLEMENTED;
	FX_ENTRY ("CCConferenceCallback ");
	CH323Ctrl *pConnection = (CH323Ctrl *)dwConferenceToken;

	if(IsBadWritePtr(pConnection, sizeof(CH323Ctrl)))
	{
		ERRORMESSAGE(("%s:invalid conf token: 0x%08lx\r\n",_fx_, dwConferenceToken));
		return CC_NOT_IMPLEMENTED;	// must be either CC_NOT_IMPLEMENTED or CC_OK.
	}
	
	if(pConnection && pConnection->GetConfHandle() == hConference)
	{

		if(pConnection->IsReleasing())
		{
			// we are in the cleanup path.  The object is being deleted without
			// waiting for asynchronous stuff to complete, and we called that one
			// final API (most likely Hangup()) that resulted in a callback.  Don't call
			// back into the object.
			DEBUGMSG(ZONE_CONN,("%s:callback while releasing:0x%08lx, hconf:0x%08lx\r\n",_fx_,
				pConnection, hConference));
			return hr;
		}	
		pConnection->AddRef();	// protect against Release()ing while not in
								// a quiescent state.  We do not want to be
								// released while inside ourself
		hr = pConnection->ConfCallback(bIndication, hConfStatus, pConferenceCallbackParams);
		pConnection->Release();
	}
	#ifdef DEBUG
	else
	{	
		if(pConnection)
			DEBUGMSG(ZONE_CONN,("%s:hConference mismatch, hConference:0x%08lx, object hconf:0x%08lx, pObject:0x%08lx\r\n",_fx_,
				hConference, pConnection->GetConfHandle(), pConnection));
		else
			DEBUGMSG(ZONE_CONN,("%s:null dwConferenceToken\r\n",_fx_));
	}
	#endif //DEBUG
	return hr;
}

VOID  CCListenCallback (HRESULT hStatus,PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams)
{
	FX_ENTRY ("CCListenCallback");
	CH323Ctrl *pConnection;
	if(!pListenCallbackParams)
	{
		return;
	}
	pConnection = (CH323Ctrl *)pListenCallbackParams->dwListenToken;

	if(IsBadWritePtr(pConnection, sizeof(CH323Ctrl)))
	{
		ERRORMESSAGE(("%s:invalid listen token: 0x%08lx\r\n",_fx_, pListenCallbackParams->dwListenToken));
		return;
	}

	// BUGBUG there's no hListen passed in - we can't validate it
	//	if(pConnection && (pConnection->GetListenHandle() == pListenCallbackParams->h??????))

	if(pConnection)
	{
		pConnection->AddRef();	// protect against Release()ing while not in
								// a quiescent state.  We do not want to be
								// released while inside ourself
		pConnection->ListenCallback(hStatus,pListenCallbackParams);
		pConnection->Release();
	}
	else
	{
		ERRORMESSAGE(("%s:null listen token\r\n",_fx_));
	}

}
VOID CH323Ctrl::ListenCallback (HRESULT hStatus,PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams)
{
	FX_ENTRY ("CH323Ctrl::ListenCallback");
	HRESULT hr;
	if(hStatus != CC_OK)
	{
		m_hCallCompleteCode = CCCI_LOCAL_ERROR;
		CH323Ctrl *	pAcceptingConnection = NULL;
		BOOL bDisconnect = FALSE;

		ERRORMESSAGE(("%s:error 0x%08lx\r\n",_fx_,hStatus));
		// aaaaghhh!!! an unsolicited error!!!!!
		// MikeV 10/12/96 - observed behavior is that this will occur if the caller disconnects
		// before the call is accepted (or during acceptance - if a BP is set before the call
		// to AcceptRejectConnection(), the caller times out.  But even after that, tracing
		// over AcceptRejectConnection() shown no error is returned. This is bad, because
		// it is hard to tell if this error needs cleaning up after.  The error code in
		// that case is 0xa085a001, which is CC_PEER_REJECT

		// We also don't know if another object has been created to accept the connection
		// or if this is being called in the context of the object that was created and
		// its handle passed to AcceptRejectConnection().  The typical behavior is that it
		// is called in the context of the listening object.

		// once the accepting object is located, need to check state to see if
		// connection is in the process of being accepted.  Find accepting object
		// by matching pListenCallbackParams->ConferenceID;	
		
		// see if this is the correct context
		if(memcmp(&pListenCallbackParams->ConferenceID, &m_ConferenceID, sizeof(m_ConferenceID))==0)
		{
			// check the current state.  If in the process of accepting
			// (either Idle, or filtering), change state to CCS_Closing to make
			// cleanup occur.  If already accepted (accepting or ringing), initiate
			// InternalDisconnect().  This should never happen in any other state.

			// EnterCriticalSection()	// LOOKLOOK - NYI
			switch(m_Phase)
			{
				case CCS_Idle:
				case CCS_Filtering:
				break;
				default:
				case CCS_Ringing:
				case CCS_Accepting:
					bDisconnect = TRUE;
					switch(hStatus)
					{
						case  CC_PEER_REJECT:
							m_hCallCompleteCode = CCCI_REJECTED;
							ERRORMESSAGE(("%s:Received CC_PEER_REJECT in state %d\r\n",_fx_,m_Phase));
						break;

						default:
						case  CC_INTERNAL_ERROR:
							m_hCallCompleteCode = CCCI_LOCAL_ERROR;
		 				break;
						
					}
				
				break;

			}
			// ExitCriticalSection()
			if(bDisconnect)
					InternalDisconnect();
		}
		else
		{
			hr = m_pConfAdvise->FindAcceptingObject((LPIControlChannel *)&pAcceptingConnection,
				&pListenCallbackParams->ConferenceID);
			if(HR_SUCCEEDED(hr) && pAcceptingConnection)
			{
				// call this function in the correct context
				pAcceptingConnection->AddRef();
				pAcceptingConnection->ListenCallback (hStatus, pListenCallbackParams);
				pAcceptingConnection->Release();
			}
			else
			{
					ERRORMESSAGE(("%s:conference ID 0x%08lx 0x%08lx 0x%08lx 0x%08lx\r\n"
						,_fx_,pListenCallbackParams->ConferenceID.buffer[0],
						pListenCallbackParams->ConferenceID.buffer[4],
						pListenCallbackParams->ConferenceID.buffer[8],
						pListenCallbackParams->ConferenceID.buffer[12]));
					ERRORMESSAGE(("%s:Received 0x%08lx in state %d, accepting object not found\r\n"
					,_fx_,hStatus, m_Phase));
			}
		}
		
		return;
	}
	// non error case falls out
	switch(pListenCallbackParams->wGoal)
	{
		default:
		case CC_GOAL_UNKNOWN:
		break;
		
		case CC_GOAL_CREATE:
		case CC_GOAL_JOIN:
		case CC_GOAL_INVITE:
			m_ConferenceID = pListenCallbackParams->ConferenceID;	
			m_hCall =  pListenCallbackParams->hCall;

			if(pListenCallbackParams->pCallerAliasNames || pListenCallbackParams->pszDisplay)
			{			
				NewRemoteUserInfo(pListenCallbackParams->pCallerAliasNames,
					pListenCallbackParams->pszDisplay);
			}
			else
			{
				ERRORMESSAGE(("%s:null pListenCallbackParams->pCallerAliasNames\r\n",_fx_));
			}
			
			if(!OnCallAccept(pListenCallbackParams))
			{
				ERRORMESSAGE(("ListenCallback:OnCallAccept failed\r\n"));
			}
						
		break;
	}
}


//
//  Main conference indication dispatcher
//
#ifdef DEBUG
TCHAR *i_strs[ ] =
{
"ERROR! - INDICATION ZERO",
"CC_RINGING_INDICATION",
"CC_CONNECT_INDICATION", 						
"CC_TX_CHANNEL_OPEN_INDICATION",				
"CC_RX_CHANNEL_REQUEST_INDICATION",			
"CC_RX_CHANNEL_CLOSE_INDICATION",		
"CC_MUTE_INDICATION",			
"CC_UNMUTE_INDICATION",						
"CC_PEER_ADD_INDICATION",						
"CC_PEER_DROP_INDICATION",						
"CC_PEER_CHANGE_CAP_INDICATION",
"CC_CONFERENCE_TERMINATION_INDICATION",
"CC_HANGUP_INDICATION",					
"CC_RX_NONSTANDARD_MESSAGE_INDICATION",
"CC_MULTIPOINT_INDICATION",	
"CC_PEER_UPDATE_INDICATION",				
"CC_H245_MISCELLANEOUS_COMMAND_INDICATION",
"CC_H245_MISCELLANEOUS_INDICATION_INDICATION",
"CC_H245_CONFERENCE_REQUEST_INDICATION",
"CC_H245_CONFERENCE_RESPONSE_INDICATION",
"CC_H245_CONFERENCE_COMMAND_INDICATION",	
"CC_H245_CONFERENCE_INDICATION_INDICATION",
"CC_FLOW_CONTROL_INDICATION",
"CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION",
"CC_REQUEST_MODE_INDICATION",	
"CC_REQUEST_MODE_RESPONSE_INDICATION",
"CC_VENDOR_ID_INDICATION",			
"CC_MAXIMUM_AUDIO_VIDEO_SKEW_INDICATION",
"CC_T120_CHANNEL_REQUEST_INDICATION",	
"CC_T120_CHANNEL_OPEN_INDICATION",			
"CC_BANDWIDTH_CHANGED_INDICATION",
"CC_ACCEPT_CHANNEL_INDICATION",
"CC_TERMINAL_ID_REQUEST_INDICATION",
"CC_PING_RESPONSE_INDICATION",
"CC_TERMINAL_NUMBER_INDICATION"
};
#endif	//DEBUG

HRESULT CH323Ctrl::ConfCallback (BYTE bIndication,
	HRESULT	hStatus, PCC_CONFERENCE_CALLBACK_PARAMS pConferenceCallbackParams)
{
	FX_ENTRY ("CH323Ctrl::ConfCallback");
	HRESULT hr = CC_NOT_IMPLEMENTED;
	DEBUGMSG(ZONE_CONN,("%s: %s\r\n", _fx_, i_strs[bIndication]));

	SHOW_OBJ_ETIME(i_strs[bIndication]);

	switch (bIndication)
	{
		case CC_RINGING_INDICATION:
			// (PCC_RINGING_CALLBACK_PARAMS) pConferenceCallbackParams;
			// user info may be available now and it may not be
			OnCallRinging(hStatus, (PCC_RINGING_CALLBACK_PARAMS) pConferenceCallbackParams);
			
		break;
		case CC_CONNECT_INDICATION:
			OnCallConnect(hStatus, (PCC_CONNECT_CALLBACK_PARAMS) pConferenceCallbackParams);
			hr = CC_OK;
		break;
		case CC_PEER_ADD_INDICATION:
		case CC_PEER_UPDATE_INDICATION:
		case CC_PEER_DROP_INDICATION:
		case CC_TERMINAL_NUMBER_INDICATION:
		break;
		
		case CC_HANGUP_INDICATION:
			OnHangup(hStatus);
			hr = CC_OK;
		break;
		case CC_CONFERENCE_TERMINATION_INDICATION:
		// September 1996 comments:
		// I don't know if there will also be a CC_HANGUP_INDICATION after this.
		// We're going to call Hangup() via Disconnect()
		// December 1996: Hangup() (excuse me, CC_Hangup()) no longer gives back a
		// CC_HANGUP_INDICATION in this state.  It returns an error.  The new behavior
		// seems to indicate that the call control channel is already dead at this point
		// so, set our flags as such!!!
			m_ChanFlags &= ~(CTRLF_OPEN);
			//set state to indicate disconnecting.
			GoNextPhase(CCS_Disconnecting);
			DoAdvise(CCEV_REMOTE_DISCONNECTING ,NULL);
			GoNextPhase(CCS_Idle);	// no need to ck retval - we're disconnected
				// notify the UI or application code or whatever..
			DoAdvise(CCEV_DISCONNECTED ,NULL);
			hr = CC_OK;
		break;
		case CC_PEER_CHANGE_CAP_INDICATION:
		break;
		
		//
		// Channel stuff
		//
		case CC_TX_CHANNEL_OPEN_INDICATION:
			OnChannelOpen(hStatus,(PCC_TX_CHANNEL_OPEN_CALLBACK_PARAMS)pConferenceCallbackParams);
			hr = CC_OK;
		break;
		case CC_RX_CHANNEL_REQUEST_INDICATION:
			OnChannelRequest(hStatus, (PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
			hr = CC_OK;			
		break;
		
		// the following 4 channel-centric indications have the same basic parameter
		// structure.  When we get the final Intel drop, we can clean it up. 1 - collapse
		// the parameters into a common "channel indication" structure. 2 - make sure
		// that a user pointer is stored in that structure for easy finding of channel
		// context.  3 - collapse separate channel event handling functions into one.
		case CC_MUTE_INDICATION:
		    OnMute(hStatus, (PCC_MUTE_CALLBACK_PARAMS)pConferenceCallbackParams);
        	hr = CC_OK;	
		break;
		case CC_UNMUTE_INDICATION:
		    OnUnMute(hStatus, (PCC_UNMUTE_CALLBACK_PARAMS)pConferenceCallbackParams);
        	hr = CC_OK;	
		break;
		case CC_RX_CHANNEL_CLOSE_INDICATION:
			OnRxChannelClose(hStatus,(PCC_RX_CHANNEL_CLOSE_CALLBACK_PARAMS)pConferenceCallbackParams);
			hr = CC_OK;
		break;
		case CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION:
			OnTxChannelClose(hStatus,(PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
			hr = CC_OK;
		// CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION callback parameters (pConferenceCallbackParams)
		//typedef struct {
		//	CC_HCHANNEL				hChannel;
		//} CC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS, *PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS;
		break;
		case CC_FLOW_CONTROL_INDICATION:
		// CC_FLOW_CONTROL_INDICATION callback parameters (pConferenceCallbackParams)
		// typedef struct {
		//		CC_HCHANNEL				hChannel;
		//		DWORD					dwRate;
		//	} CC_FLOW_CONTROL_CALLBACK_PARAMS, *PCC_FLOW_CONTROL_CALLBACK_PARAMS;
		break;	
		
		case CC_BANDWIDTH_CHANGED_INDICATION:
		case CC_REQUEST_MODE_INDICATION:
		case CC_REQUEST_MODE_RESPONSE_INDICATION:
		break;
		
		case CC_ACCEPT_CHANNEL_INDICATION:
			hr = CC_OK;	
			OnChannelAcceptComplete(hStatus, (PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
		break;
		//
		//	Misc commands and indications.  Some are related to channels
		//
		case CC_RX_NONSTANDARD_MESSAGE_INDICATION:
		break;
		case CC_H245_MISCELLANEOUS_COMMAND_INDICATION:
			OnMiscCommand(hStatus,
				(PCC_H245_MISCELLANEOUS_COMMAND_CALLBACK_PARAMS)pConferenceCallbackParams);
		break;
		case CC_H245_MISCELLANEOUS_INDICATION_INDICATION: // from the Department of Redundancy Department
			OnMiscIndication(hStatus,
				(PCC_H245_MISCELLANEOUS_INDICATION_CALLBACK_PARAMS)pConferenceCallbackParams);
		break;
		case CC_T120_CHANNEL_REQUEST_INDICATION:
			OnT120ChannelRequest(hStatus,(PCC_T120_CHANNEL_REQUEST_CALLBACK_PARAMS)pConferenceCallbackParams);
		break;
		case CC_T120_CHANNEL_OPEN_INDICATION:
			OnT120ChannelOpen(hStatus,(PCC_T120_CHANNEL_OPEN_CALLBACK_PARAMS)pConferenceCallbackParams);
		default:
		break;
	}
	return hr;

}


VOID CH323Ctrl::OnT120ChannelRequest(
	HRESULT hStatus,
	PCC_T120_CHANNEL_REQUEST_CALLBACK_PARAMS pT120RequestParams)
{
	FX_ENTRY ("CH323Ctrl::OnT120ChannelRequest");
	PSOCKADDR_IN	pAddr;
	SOCKADDR_IN		sinD;
	CC_ADDR ChannelAddr;
	PCC_ADDR pChannelAddr;
	GUID mediaID;
	DWORD dwRejectReason = H245_REJ;
	BOOL bFound = FALSE;
	POSITION pos = m_ChannelList.GetHeadPosition();	
	ICtrlCommChan *pChannel = NULL;

	// look for a matching channel instance.
	while (pos)
	{
		pChannel = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
		ASSERT(pChannel);

		hrLast = pChannel->GetMediaType(&mediaID);
		if(!HR_SUCCEEDED(hrLast))
			goto ERROR_EXIT;
		if(mediaID == MEDIA_TYPE_H323_T120)
		{
			bFound = TRUE;
			break;
		}
	}

	if(!HR_SUCCEEDED(hrLast) || !bFound)
	{
		// Non-default channels Not Yet Implemented!!!!
		// When it is, ask the parent conference object	to create another channel of the
		// specified media type.
		if(hrLast == CCO_E_NODEFAULT_CHANNEL)
			dwRejectReason = H245_REJ_TYPE_NOTAVAIL;

		goto REJECT_CHANNEL;
	}

	// if we are the H.245 master and have requested a T.120 channel already,
	// reject this request.
	if(m_ConferenceAttributes.bMaster && pChannel->GetHChannel())
	{
		goto REJECT_CHANNEL;
	}
	if(!pChannel->IsChannelEnabled())	//   allow this channel ?
	{
		goto REJECT_CHANNEL;
	}

	pChannel->SetHChannel(pT120RequestParams->hChannel);
	if(pT120RequestParams->pAddr)
	{
		// the other end is listening on the specified address
		sinD.sin_family = AF_INET;
		sinD.sin_addr.S_un.S_addr = htonl(pT120RequestParams->pAddr->Addr.IP_Binary.dwAddr);
		sinD.sin_port = htons(pT120RequestParams->pAddr->Addr.IP_Binary.wPort);
		
		DEBUGMSG(ZONE_CONN,("%s, requestor listening on port 0x%04x, address 0x%08lX\r\n",_fx_,
			pT120RequestParams->pAddr->Addr.IP_Binary.wPort,
			pT120RequestParams->pAddr->Addr.IP_Binary.dwAddr));

		hrLast = pChannel->AcceptRemoteAddress(&sinD);
		pChannelAddr = NULL;
	}
	else
	{
		// the channel selects its local address(es)/port(s)
		if(!pChannel->SelectPorts((LPIControlChannel)this))
		{
			ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
			hrLast = CCO_E_BAD_ADDRESS;
			goto REJECT_CHANNEL;
		}
		// get the address and ports of our end of the channel
		pAddr = pChannel->GetLocalAddress();
		// fixup channel addr pair structure.
		ChannelAddr.nAddrType = CC_IP_BINARY;
		ChannelAddr.bMulticast = FALSE;
		ChannelAddr.Addr.IP_Binary.wPort = ntohs(pAddr->sin_port);
		ChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
		pChannelAddr = &ChannelAddr;
		DEBUGMSG(ZONE_CONN,("%s: accepting on port 0x%04x, address 0x%08lX\r\n",_fx_,
			ChannelAddr.Addr.IP_Binary.wPort,ChannelAddr.Addr.IP_Binary.dwAddr));
	}
	
	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest accepting");

	hrLast = CC_AcceptT120Channel(
		pChannel->GetHChannel(),
		FALSE,	// BOOL bAssociateConference,
		NULL, 	// PCC_OCTETSTRING					pExternalReference,
		pChannelAddr);

	if(hrLast != CC_OK)
	{
		ERRORMESSAGE(("%s, CC_AcceptT120Channel returned 0x%08lX\r\n",_fx_, hrLast));
		goto ERROR_EXIT;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest accepted");

	// LOOKLOOK !!! the 2 following lines would not be there because we should
	// Wait for CC_ACCEPT_CHANNEL_INDICATION.  But the CC_ACCEPT_CHANNEL_INDICATION
	// is missing if a send audio and send video channel is open at the time this
	// channel is accepted.  A bug in CALLCONT.DLL that needs investigating.
	hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN);	
	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest, open done");

	// ******	
	// LOOKLOOK if OnChannelOpen returns an error, need to close the channel
	// but pChannel->Close() is not yet implemented for bidirectional channels	
	// ******
	
	m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_BIDI, pChannel, this);			
	//
	//	Check for readiness to notify that all required channels are open
	//
	CheckChannelsReady( );	//
	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelRequest done");

	return;

REJECT_CHANNEL:	
	{
	// need private HRESULT! don't overwrite the reason we're rejecting the channel!!	
		HRESULT hr;	
		ERRORMESSAGE(("%s, rejecting channel\r\n",_fx_));
	
		hr = CC_RejectChannel(pT120RequestParams->hChannel, dwRejectReason);
		if(hr != CC_OK)
		{
			ERRORMESSAGE(("%s, CC_RejectChannel returned 0x%08lX\r\n",_fx_, hr));
		}
	}	
ERROR_EXIT:
	return;
}

VOID CH323Ctrl::OnT120ChannelOpen(
	HRESULT hStatus,
	PCC_T120_CHANNEL_OPEN_CALLBACK_PARAMS pT120OpenParams)
{
	FX_ENTRY ("CH323Ctrl::OnT120ChannelOpen");
	SOCKADDR_IN sinD;
	GUID mediaID;
	ICtrlCommChan *pChannel = (ICtrlCommChan *)pT120OpenParams->dwUserToken;	
	// validate channel token - is this what we think it is?
	if(IsBadWritePtr(pChannel, sizeof(ICtrlCommChan)))
	{
		ERRORMESSAGE(("%s:invalid channel token: 0x%08lx\r\n",_fx_, pT120OpenParams->dwUserToken));
		return;
	}

#ifdef DEBUG
	POSITION pos = m_ChannelList.GetHeadPosition();	
	ICtrlCommChan *pChan;
	BOOL bValid = FALSE;
	// look for a matching channel instance.
	while (pos)
	{
		pChan = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
		ASSERT(pChan);
		if(pChan == pChannel)
		{
			bValid = TRUE;
			break;
		}
	}
	if(!bValid)
	{
		ERRORMESSAGE(("%s:unrecognized token 0x%08lX\r\n",_fx_,
			pT120OpenParams->dwUserToken));
		return;
	}
#endif	//DEBUG

	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen");
	
	if(hStatus != CC_OK)
	{
		DEBUGMSG(ZONE_CONN,("%s: hStatus:0x%08lX\r\n",_fx_,hStatus));
		// LOOKLOOK need to interpret hStatus
		// let the channel know what happened.

		// if the request was rejected due to a collision of T.120 O.L.C. requests,
		// (other end is the master and other end also requested a T.120 channel)
		// then proceed with the call.

		if(m_ConferenceAttributes.bMaster)
		{
			// the slave would only reject in a real error condition
			pChannel->OnChannelOpen(CHANNEL_REJECTED);	
			// the channel knows what happened, so let it do the worrying.
			return;

		}
		else	// just a typical collision
		{
			return;
		}
	}
	// if the other end specified its listen address, use it
	if(pT120OpenParams->pAddr)
	{
		if(pT120OpenParams->pAddr->nAddrType != CC_IP_BINARY)
		{
			ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,
					pT120OpenParams->pAddr->nAddrType));
			goto ERROR_EXIT;
		}	
		
		// we now have the remote port info ( in host byte order)
		sinD.sin_family = AF_INET;
		sinD.sin_addr.S_un.S_addr = htonl(pT120OpenParams->pAddr->Addr.IP_Binary.dwAddr);
		sinD.sin_port = htons(pT120OpenParams->pAddr->Addr.IP_Binary.wPort);
		
		DEBUGMSG(ZONE_CONN,("%s, opened on port 0x%04x, address 0x%08lX\r\n",_fx_,
			pT120OpenParams->pAddr->Addr.IP_Binary.wPort,pT120OpenParams->pAddr->Addr.IP_Binary.dwAddr));

		hrLast = pChannel->AcceptRemoteAddress(&sinD);
		if(!HR_SUCCEEDED(hrLast))
		{
			ERRORMESSAGE(("%s:AcceptRemoteAddress failed\r\n",_fx_));
			goto ERROR_EXIT;
		}
	}
	
	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen opening");

	hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN);	
	if(!HR_SUCCEEDED(hrLast))
	{
		ERRORMESSAGE(("%s:channel's OnChannelOpen() returned 0x%08lX\r\n", _fx_, hrLast));
		CloseChannel(pChannel);
		goto ERROR_EXIT;
	}

	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen open done");
	
	m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_BIDI, pChannel, this);	
	
	//
	//	Check for readiness to notify that all required channels are open
	//
	CheckChannelsReady( );	
	SHOW_OBJ_ETIME("CH323Ctrl::OnT120ChannelOpen done");
	return;
	
ERROR_EXIT:
	// need to cleanup, disconnect, etc.
	m_hCallCompleteCode = CCCI_CHANNEL_OPEN_ERROR;
	// let the parent Conference object know about the imminent disconnect
	DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
	hrLast = CCO_E_MANDATORY_CHAN_OPEN_FAILED;

	InternalDisconnect();
	return;

}

//
//	This once did something. Currently, it's called whenever a channel is opened.  The
//  call to GoNextPhase(CCS_Ready) changes state and posts a notification upward, but
//  that notification is currently ignored.  (it's useless)
//  Reminder to mikev: A new notification is needed to indicate that capabilities
//  have been exchanged and it is OK to open channels.
//
VOID CH323Ctrl::CheckChannelsReady()
{
	GoNextPhase(CCS_Ready);
}
// handles local hangup indication
VOID CH323Ctrl::OnHangup(HRESULT hStatus)
{
	FX_ENTRY ("CH323Ctrl::OnHangup");
	DEBUGMSG(ZONE_CONN,("%s:CC_HANGUP_INDICATION in phase %d\r\n", _fx_, m_Phase));
	switch(m_Phase)
	{
		case CCS_Disconnecting:
			GoNextPhase(CCS_Idle);
			Cleanup();
			DoAdvise(CCEV_DISCONNECTED ,NULL);
		break;
		
		default: // do nothing
			ERRORMESSAGE(("%s:Unexpected CC_HANGUP_INDICATION\r\n",_fx_));
		break;
	}
}

HRESULT CH323Ctrl::CloseChannel(ICtrlCommChan* pChannel)
{
	FX_ENTRY ("CH323Ctrl::CloseChannel");
	if(!pChannel->IsChannelOpen())
	{
		ERRORMESSAGE(("%s: channel is not open\r\n",_fx_));
		hrLast = CCO_E_INVALID_PARAM;
		goto EXIT;
	}

	hrLast = CC_CloseChannel(pChannel->GetHChannel());
	if(!HR_SUCCEEDED(hrLast))
	{	
		ERRORMESSAGE(("%s: CC_CloseChannel returned 0x%08lX\r\n",_fx_, hrLast));
		goto EXIT;
	}
	// make the channel handle its own media stream specific shutdown and cleanup chores
	hrLast = pChannel->OnChannelClose(CHANNEL_CLOSED);	
	
EXIT:
	return hrLast;
}

HRESULT CH323Ctrl::AddChannel(ICtrlCommChan * pCommChannel, LPIH323PubCap pCapabilityResolver)
{
	ICtrlCommChan *pChan = NULL;


	// get the ICtrlCommChannel interface of each channel
	hrLast = pCommChannel->QueryInterface(IID_ICtrlCommChannel,(void **)&pChan);
	if(!HR_SUCCEEDED(hrLast))
		goto ADD_ERROR;
	
	// make the channel aware of its new scope
	hrLast = pChan->BeginControlSession(this, pCapabilityResolver);
	if(!HR_SUCCEEDED(hrLast))
		goto ADD_ERROR;
	// add it to the list			
	m_ChannelList.AddTail(pChan);
	return hrSuccess;

ADD_ERROR:
	if(pChan)
		pChan->Release();
	return CHAN_E_INVALID_PARAM;

}



HRESULT CH323Ctrl::OpenChannel(ICtrlCommChan* pChan, IH323PubCap *pCapResolver,
	MEDIA_FORMAT_ID dwIDLocalSend, MEDIA_FORMAT_ID dwIDRemoteRecv)
{
	FX_ENTRY ("CH323Ctrl::OpenChannel");
	CC_TERMCAP				H245ChannelCap;
	PSOCKADDR_IN			pAddr;
	CC_ADDR 				ChannelAddr;
	LPVOID pChannelParams;
	PCC_TERMCAP pSaveChannelCapability = NULL;
	UINT uLocalParamSize;
	BYTE SessionID;
	BYTE payload_type;
	DWORD_PTR dwhChannel;
	GUID mediaID;

	ASSERT((pChan->IsChannelOpen()== FALSE) && (pChan->IsOpenPending()== FALSE));
	hrLast = pChan->GetMediaType(&mediaID);
	if(!HR_SUCCEEDED(hrLast))
		goto CHANNEL_ERROR;
		
	if (mediaID == MEDIA_TYPE_H323_T120)
	{
		if(pChan->GetHChannel())	// already accepted a T.120 channel?
		{
			ERRORMESSAGE(("%s, already have a pending channel\r\n",_fx_));
			goto CHANNEL_ERROR;	// this is not an error, excuse the label
		}

		// test the no common capability case.  notify the conference object of the
		// inability to open the channel, and return success
		
		if(dwIDLocalSend == INVALID_MEDIA_FORMAT)
		{
			pChan->OnChannelOpen(CHANNEL_NO_CAPABILITY);
			return hrSuccess;
		}
		// There is no "standard" rule regarding which end specifies the "listen"
		// address of a T.120 channel. However: we want NetMeeting-NetMeeting calls
		// to behave consistently (the "caller" always "places the T.120 call").
		// Therefore, specify the address if this end is not the originator.  That will
		// force the other end to specify it's address.
		
		if(IsOriginating(m_ChanFlags))
		{
			pAddr = NULL;	// the other end "listens" and we "connect"
		}
		else	// listen on local address
		{
			// select ports if they are not already selected
			if(!pChan->SelectPorts((LPIControlChannel)this))
			{
				ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
				hrLast = CCO_E_BAD_ADDRESS;
				goto CHANNEL_ERROR;
			}
			
			// get the address and port
			pAddr = pChan->GetLocalAddress();
			// fixup channel addr structure.
			ChannelAddr.nAddrType = CC_IP_BINARY;
			ChannelAddr.bMulticast = FALSE;
			ChannelAddr.Addr.IP_Binary.wPort = ntohs(pAddr->sin_port);
			ChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);
		}

		hrLast =  CC_OpenT120Channel(
			// CC_HCONFERENCE	hConference,
			m_hConference,
	        // PCC_HCHANNEL     phChannel,
	        &dwhChannel,
			// BOOL				bAssociateConference,
			FALSE,
			// PCC_OCTETSTRING	pExternalReference,
			NULL,
			// PCC_ADDR			pAddr,
			IsOriginating(m_ChanFlags) ? NULL : &ChannelAddr,
			// DWORD			dwChannelBitRate,
			0,
			// DWORD			dwUserToken);
			(DWORD_PTR)pChan);

		// and fall out to test hrLast, etc.
	}
	else	// is an audio or video channel
	{
		// test the no common capability case.  If the channel is mandatory,
		// return an error, else notify the conference object of the
		// inability to open the channel, and return success
		
		if((dwIDLocalSend == INVALID_MEDIA_FORMAT) ||(dwIDRemoteRecv == INVALID_MEDIA_FORMAT))
		{
			pChan->OnChannelOpen(CHANNEL_NO_CAPABILITY);
			return hrSuccess;
		}
				
		//
		//   test if we need to try to open this !!!
		//
		if(!pChan->IsChannelEnabled())
		{
			return hrSuccess;
		}
		
		SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel");
			
		// Get the remote channel parameters for
		// the send channel -  these parameters are used to request a channel
		uLocalParamSize = pCapResolver->GetLocalSendParamSize((MEDIA_FORMAT_ID)dwIDLocalSend);
		pChannelParams=MemAlloc (uLocalParamSize);
		if (pChannelParams == NULL) {
		   //Doom
		   hrLast = CCO_E_SYSTEM_ERROR;
		   goto CHANNEL_ERROR;
		}
		hrLast = pCapResolver->GetEncodeParams(
				(LPVOID)&H245ChannelCap, sizeof(H245ChannelCap),
				(LPVOID)pChannelParams, uLocalParamSize,
				(AUDIO_FORMAT_ID)dwIDRemoteRecv,
				(AUDIO_FORMAT_ID)dwIDLocalSend);
	 	if(!HR_SUCCEEDED(hrLast))
		{	
			ERRORMESSAGE(("%s: GetEncodeParams returned 0x%08lX\r\n",_fx_, hrLast));
			goto CHANNEL_ERROR;
		}

		// set session ID and payload type.  Note that payload type is relevant only for
		// dynamic payloads.  Otherwise, it must be zero.
		if (H245ChannelCap.DataType == H245_DATA_AUDIO)
		{
			payload_type = ((PAUDIO_CHANNEL_PARAMETERS)pChannelParams)->RTP_Payload;
			// Session ID is 1 for Audio, 2 for Video . H245 7.3.1 (H2250 Logical Channel Param)
		   	SessionID=1;
		}
		else if (H245ChannelCap.DataType == H245_DATA_VIDEO)
		{
			payload_type = ((PVIDEO_CHANNEL_PARAMETERS)pChannelParams)->RTP_Payload;
		 	SessionID=2;
		}
		// payload_type must be zero for fixed payload types.  Weird.
		if(!IsDynamicPayload(payload_type))
			payload_type = 0;
			
		// create a marshalled version of channel parameters and store it in the channel
		// for later reference
		if(H245ChannelCap.ClientType == H245_CLIENT_AUD_NONSTD)
		{
			// Make a flat copy of the nonstandard capability to store as the channel
			// parameter
			UINT uSize = H245ChannelCap.Cap.H245Aud_NONSTD.data.length;
			pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) +  uSize);
			if(!pSaveChannelCapability)
			{
				hrLast = CCO_E_SYSTEM_ERROR;
				goto CHANNEL_ERROR;
			}	
			// copy fixed part
			memcpy(pSaveChannelCapability, &H245ChannelCap, sizeof(CC_TERMCAP));
			// variable part follows the fixed part
			pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value	
				= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
			// copy variable part
			memcpy(pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value,
				H245ChannelCap.Cap.H245Aud_NONSTD.data.value,
				H245ChannelCap.Cap.H245Aud_NONSTD.data.length);
			// and length
			pSaveChannelCapability->Cap.H245Aud_NONSTD.data.length
				= H245ChannelCap.Cap.H245Aud_NONSTD.data.length;
			
			// make the channel remember the channel parameters.
			// a zero size as the second arg means that a preallocated chunk is being passed
			hrLast = pChan->ConfigureCapability(pSaveChannelCapability, 0,
				pChannelParams, uLocalParamSize);	
			if(!HR_SUCCEEDED(hrLast))
			{
				ERRORMESSAGE(("%s:ConfigureCapability returned 0x%08lx\r\n",_fx_, hrLast));
				hrLast = CCO_E_SYSTEM_ERROR;
				goto CHANNEL_ERROR;
			}
			pSaveChannelCapability=NULL;  // the channel owns this memory now
		}
		else if(H245ChannelCap.ClientType == H245_CLIENT_VID_NONSTD)
		{
			// Make a flat copy of the nonstandard capability to store as the channel
			// parameter
			UINT uSize = H245ChannelCap.Cap.H245Vid_NONSTD.data.length;
			pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) +  uSize);
			if(!pSaveChannelCapability)
			{
				hrLast = CCO_E_SYSTEM_ERROR;
				goto CHANNEL_ERROR;
			}	
			// copy fixed part
			memcpy(pSaveChannelCapability, &H245ChannelCap, sizeof(CC_TERMCAP));
			// variable part follows the fixed part
			pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value	
				= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
			// copy variable part
			memcpy(pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value,
				H245ChannelCap.Cap.H245Vid_NONSTD.data.value,
				H245ChannelCap.Cap.H245Vid_NONSTD.data.length);
			// and length
			pSaveChannelCapability->Cap.H245Vid_NONSTD.data.length
				= H245ChannelCap.Cap.H245Vid_NONSTD.data.length;
			
			// make the channel remember the channel parameters.
			// a zero size as the second arg means that a preallocated chunk is being passed
			hrLast = pChan->ConfigureCapability(pSaveChannelCapability, 0,
				pChannelParams, uLocalParamSize);	
			if(!HR_SUCCEEDED(hrLast))
			{
				ERRORMESSAGE(("%s:ConfigureCapability returned 0x%08lx\r\n",_fx_, hrLast));
				hrLast = CCO_E_SYSTEM_ERROR;
				goto CHANNEL_ERROR;
			}
			pSaveChannelCapability=NULL;  // the channel owns this memory now
		}
		else
		{
			// only need to remember the already-flat H.245 cap structure.
			hrLast = pChan->ConfigureCapability(&H245ChannelCap, sizeof(CC_TERMCAP),
				pChannelParams, uLocalParamSize);	
			if(!HR_SUCCEEDED(hrLast))
			{
				ERRORMESSAGE(("%s:ConfigureCapability returned 0x%08lx\r\n",_fx_, hrLast));
				hrLast = CCO_E_SYSTEM_ERROR;
				goto CHANNEL_ERROR;
			}
		}

		// remember both versions of the resolved send format for the channel
		// we're about to open	
		pChan->SetNegotiatedLocalFormat(dwIDLocalSend);	
		pChan->SetNegotiatedRemoteFormat(dwIDRemoteRecv);
		
		SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel done configuring");

		// select ports if they are not already selected
		if(!pChan->SelectPorts((LPIControlChannel)this))
		{
			ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
			hrLast = CCO_E_BAD_ADDRESS;
			goto CHANNEL_ERROR;
		}
		
		// get the address and port of our RTCP channel
		pAddr = pChan->GetLocalAddress();
		// fixup channel addr structure. There are two ports, but in RTP, it is implicit
		// that the RTCP control port is the next highest port number.
		// The open logical channel request needs the reverse RTCP port to be specified.
		ChannelAddr.nAddrType = CC_IP_BINARY;
		ChannelAddr.bMulticast = FALSE;
		ChannelAddr.Addr.IP_Binary.wPort = pChan->GetLocalRTCPPort();
		ChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);

		DEBUGMSG(ZONE_CONN,("%s: opening using RTCP port 0x%04x, address 0x%08lX\r\n",_fx_,
			ChannelAddr.Addr.IP_Binary.wPort,ChannelAddr.Addr.IP_Binary.dwAddr));
		
		DEBUGMSG(ZONE_CONN,("%s: requesting capability ID:0x%08lX\r\n",
			_fx_, H245ChannelCap.CapId));

		// open a channel
		SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel, opening");
											
		hrLast = CC_OpenChannel(m_hConference, &dwhChannel,
			SessionID,
			0,  //	BYTE bAssociatedSessionID,
			TRUE, //BOOL bSilenceSuppression,  WE ALWAYS DO SILENCE SUPPRESSION
			&H245ChannelCap,	
			&ChannelAddr, 	// the local address on which we're listening for RTCP
			payload_type,	// PAYLOAD TYPE
			0,				//	DWORD dwChannelBitRate,
			(DWORD_PTR)pChan);	// use the channel pointer as the user token
	} // end else is an audio or video channel
	
	if(hrLast != CC_OK)
	{
		ERRORMESSAGE(("%s: OpenChannel returned 0x%08lX\r\n",_fx_, hrLast));
		goto CHANNEL_ERROR;
	}		
	else
	{
		pChan->SetHChannel(dwhChannel);
		pChan->OnChannelOpening();
	}

	SHOW_OBJ_ETIME("CH323Ctrl::OpenChannel done");
	return hrLast;

CHANNEL_ERROR:
	if(pSaveChannelCapability)
		MemFree(pSaveChannelCapability);
		
	return hrLast;
}


VOID CH323Ctrl::CleanupConferenceAttributes()
{
	WORD w;
	if(m_ConferenceAttributes.pParticipantList->ParticipantInfoArray)
	{
		for(w=0;w<m_ConferenceAttributes.pParticipantList->wLength;w++)
		{	
			if(m_ConferenceAttributes.pParticipantList->
				ParticipantInfoArray[w].TerminalID.pOctetString)
			{
				MemFree(m_ConferenceAttributes.pParticipantList->
					ParticipantInfoArray[w].TerminalID.pOctetString);
			}

		}
		
		MemFree(m_ConferenceAttributes.pParticipantList->ParticipantInfoArray);
	}
	m_ConferenceAttributes.pParticipantList->ParticipantInfoArray = NULL;
	m_ConferenceAttributes.pParticipantList->wLength = 0;
	
}	

HRESULT CH323Ctrl::AllocConferenceAttributes()
{
	WORD w;
	#define MAX_PART_LEN 128
	if(m_ConferenceAttributes.pParticipantList->wLength)
	{
		m_ConferenceAttributes.pParticipantList->ParticipantInfoArray =
			(PCC_PARTICIPANTINFO) MemAlloc (sizeof(CC_PARTICIPANTINFO)
				* m_ConferenceAttributes.pParticipantList->wLength);
				
		if(!m_ConferenceAttributes.pParticipantList->ParticipantInfoArray)
			return CCO_E_OUT_OF_MEMORY;
			
		for(w=0;w<m_ConferenceAttributes.pParticipantList->wLength;w++)
		{	
			m_ConferenceAttributes.pParticipantList->
				ParticipantInfoArray[w].TerminalID.pOctetString
				= (BYTE *)MemAlloc(MAX_PART_LEN);
			if(m_ConferenceAttributes.pParticipantList->
				ParticipantInfoArray[w].TerminalID.pOctetString)
			{
				m_ConferenceAttributes.pParticipantList->
					ParticipantInfoArray[w].TerminalID.wOctetStringLength
					= MAX_PART_LEN;
			}
			else
			{
				m_ConferenceAttributes.pParticipantList->
					ParticipantInfoArray[w].TerminalID.wOctetStringLength =0;
				return CCO_E_OUT_OF_MEMORY;
			}
		}
	}
	return hrSuccess;
}

VOID CH323Ctrl::OnCallConnect(HRESULT hStatus, PCC_CONNECT_CALLBACK_PARAMS pConfParams)
{
	FX_ENTRY ("CH323Ctrl::OnCallConnect");
	PCC_TERMCAPLIST			pTermCapList;
	PCC_TERMCAPDESCRIPTORS	pTermCapDescriptors;
	CC_TERMCAP				H245ChannelCap;
	PCC_TERMCAP 			pChannelCap = NULL;
	CapsCtl *pCapabilityResolver = NULL;
	GUID mediaID;
	POSITION pos = NULL;
	ICtrlCommChan *pChan = NULL;

	if(hStatus != CC_OK)
	{
		ERRORMESSAGE(("%s hStatus=0x%08lx in phase %d\r\n",_fx_,hStatus,m_Phase));

		// test for gatekeeper admission reject
		// FACILITY_GKIADMISSION
		if(CUSTOM_FACILITY(hStatus) == FACILITY_GKIADMISSION)
		{
			// pass this code intact - do not remap
			m_hCallCompleteCode = hStatus;
		}
		else
		{
			switch (hStatus)
			{
				default:
				// reason is unknown
					m_hCallCompleteCode = CCCI_UNKNOWN;
				break;
				case  CC_PEER_REJECT:
					if(m_Phase == CCS_Connecting)
					{
						switch(pConfParams->bRejectReason)
						{
							case CC_REJECT_ADAPTIVE_BUSY:
							case CC_REJECT_IN_CONF:
							case CC_REJECT_USER_BUSY:
								m_hCallCompleteCode = CCCI_BUSY;
							break;
							case CC_REJECT_SECURITY_DENIED:
								m_hCallCompleteCode = CCCI_SECURITY_DENIED;
							break;
							case CC_REJECT_NO_ANSWER:
							case CC_REJECT_TIMER_EXPIRED:
								m_hCallCompleteCode = CCCI_NO_ANSWER_TIMEOUT;
							break;
							case CC_REJECT_GATEKEEPER_RESOURCES:
								m_hCallCompleteCode = CCCI_GK_NO_RESOURCES;
							break;
							default:
								//#define CC_REJECT_NO_BANDWIDTH              1
								//#define CC_REJECT_GATEKEEPER_RESOURCES      2
								//#define CC_REJECT_UNREACHABLE_DESTINATION   3
								//#define CC_REJECT_DESTINATION_REJECTION     4
								//#define CC_REJECT_INVALID_REVISION          5
								//#define CC_REJECT_NO_PERMISSION             6
								//#define CC_REJECT_UNREACHABLE_GATEKEEPER    7
								//#define CC_REJECT_GATEWAY_RESOURCES         8
								//#define CC_REJECT_BAD_FORMAT_ADDRESS        9
								//#define CC_REJECT_ROUTE_TO_GATEKEEPER       12
	// would be nice to handle this -->> //#define CC_REJECT_CALL_FORWARDED            13
								//#define CC_REJECT_ROUTE_TO_MC               14
								//#define CC_REJECT_UNDEFINED_REASON          15
								//#define CC_REJECT_INTERNAL_ERROR            16    // Internal error occured in peer CS stack.
								//#define CC_REJECT_NORMAL_CALL_CLEARING      17    // Normal call hangup
								//#define CC_REJECT_NOT_IMPLEMENTED           20    // Service has not been implemented
								//#define CC_REJECT_MANDATORY_IE_MISSING      21    // Pdu missing mandatory ie
								//#define CC_REJECT_INVALID_IE_CONTENTS       22    // Pdu ie was incorrect
								//#define CC_REJECT_CALL_DEFLECTION           24    // You deflected the call, so lets quit.
								//#define CC_REJECT_GATEKEEPER_TERMINATED     25    // Gatekeeper terminated call

								m_hCallCompleteCode = CCCI_REJECTED;
							break;
						}
					}
					else
					{
						ERRORMESSAGE(("%s:Received CC_PEER_REJECT in state %d\r\n",_fx_,m_Phase));
					}
				break;
				case  CC_INTERNAL_ERROR:
					m_hCallCompleteCode = CCCI_LOCAL_ERROR;
				break;

			}
		}
		// let the parent Conference object know  (unless this is the answering end)
		if(m_Phase == CCS_Connecting)
		{
			DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
		}

		InternalDisconnect();
		return;
	}
	else if(!pConfParams)
	{
		ERRORMESSAGE(("OnCallConnect: null pConfParams\r\n"));
		m_hCallCompleteCode = CCCI_LOCAL_ERROR;
		DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
		InternalDisconnect();
		return;
	}
	
    SetRemoteVendorID(pConfParams->pVendorInfo);

	GoNextPhase(CCS_Opening);
	m_ChanFlags |= (CTRLF_OPEN);
	DEBUGMSG(ZONE_CONN,("%s:CONNECTION_CONNECTED\r\n", _fx_));
	if((!pConfParams->pLocalAddr) || (pConfParams->pLocalAddr->nAddrType != CC_IP_BINARY))
	{
		if(pConfParams->pLocalAddr)
		{
			ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,pConfParams->pLocalAddr->nAddrType));
		}
		else
		{
			ERRORMESSAGE(("%s: null local address\r\n",_fx_));
		}
	
		ERRORMESSAGE(("%s:where's the local address????\r\n",_fx_));
					PHOSTENT phe;
					PSOCKADDR_IN psin;
				 	char szTemp[200];
					LPCSTR lpHostName;		
					gethostname(szTemp,sizeof(szTemp));
			    	lpHostName = szTemp;
					psin = &local_sin;
					phe = gethostbyname(lpHostName);
					if (phe != NULL)
					{
				   		memcpy((char FAR *)&(psin->sin_addr), phe->h_addr,phe->h_length);
						psin->sin_family = AF_INET;
					}
		
	}	
	else
	{
		// remember our local address
		local_sin.sin_family = AF_INET;
		// in host byte order
		local_sin.sin_addr.S_un.S_addr = htonl(pConfParams->pLocalAddr->Addr.IP_Binary.dwAddr);
		// in host byte order
		local_sin.sin_port = htons(pConfParams->pLocalAddr->Addr.IP_Binary.wPort);
	}
	DEBUGMSG(ZONE_CONN,("%s local port 0x%04x, address 0x%08lX\r\n",_fx_,
	local_sin.sin_port,local_sin.sin_addr.S_un.S_addr));	
	
	// get remote address
	if((!pConfParams->pPeerAddr) || (pConfParams->pPeerAddr->nAddrType != CC_IP_BINARY))
	{
		if(pConfParams->pPeerAddr)
		{
			ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,pConfParams->pPeerAddr->nAddrType));
		}
		else
		{
			ERRORMESSAGE(("%s: null local address\r\n",_fx_));
		}	
	}
	else
	{
		// remember the remote peer address
		remote_sin.sin_family = AF_INET;
		// in host byte order
		remote_sin.sin_addr.S_un.S_addr = htonl(pConfParams->pPeerAddr->Addr.IP_Binary.dwAddr);
		// in host byte order
		remote_sin.sin_port = htons(pConfParams->pPeerAddr->Addr.IP_Binary.wPort);
	}
//
// The only available remote user information in this state is the Q.931 display name.
// If we are the callee, we got the caller alias name (wire format was unicode) in
// the listen callback parameters.  If we are the caller, we really need the callee
// alias name(s), which are not propagated.   Fallback to the Q.931 display name (ASCII)
//

	NewRemoteUserInfo(NULL, pConfParams->pszPeerDisplay);

	// release any stale memory, reset ConferenceAttributes struture
	CleanupConferenceAttributes();
	// get the number of conference participants etc.
	SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect getting attribs 1");

	hrLast = CC_GetConferenceAttributes(m_hConference, &m_ConferenceAttributes);
	if(!HR_SUCCEEDED(hrLast))
	{// fatal error
		ERRORMESSAGE(("%s,CC_GetConferenceAttributes returned 0x%08lX\r\n", _fx_, hrLast));
		goto CONNECT_ERROR;

	}
	hrLast = AllocConferenceAttributes();
	if(!HR_SUCCEEDED(hrLast))
	{// fatal error
		ERRORMESSAGE(("%s,AllocConferenceAttributes returned 0x%08lX\r\n", _fx_, hrLast));
		goto CONNECT_ERROR;

	}
	// now get the real attributes
	SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect getting attribs 2");
	hrLast = CC_GetConferenceAttributes(m_hConference, &m_ConferenceAttributes);
	if(!HR_SUCCEEDED(hrLast))
	{// fatal error
		ERRORMESSAGE(("%s,CC_GetConferenceAttributes returned 0x%08lX\r\n", _fx_, hrLast));
		goto CONNECT_ERROR;

	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect got attribs");
	
	m_ConferenceID =m_ConferenceAttributes.ConferenceID;
	m_bMultipointController = m_ConferenceAttributes.bMultipointController;

	hrLast = m_pConfAdvise->GetCapResolver((LPVOID *)&pCapabilityResolver, OID_CAP_ACM_TO_H323);
	if(!HR_SUCCEEDED(hrLast) || (pCapabilityResolver == NULL))
	{// fatal error
		ERRORMESSAGE(("%s,null resolver\r\n", _fx_));
		goto CONNECT_ERROR;

	}
		
	// get the remote capabilities
	// cache the remote capabilities now
	pTermCapList = pConfParams->pTermCapList;
	pTermCapDescriptors = pConfParams->pTermCapDescriptors;
	hrLast = pCapabilityResolver->AddRemoteDecodeCaps(pTermCapList, pTermCapDescriptors, &m_RemoteVendorInfo);
	if(!HR_SUCCEEDED(hrLast))
	{// fatal error
		ERRORMESSAGE(("%s,AddRemoteDecodeCaps returned 0x%08lX\r\n", _fx_, hrLast));
		goto CONNECT_ERROR;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect saved caps");
	DoAdvise(CCEV_CAPABILITIES_READY, NULL);	// put connobj in a state to allow other
												// channels to be added & opened
	//	
	//	 notify UI here.  It wants remote user info.
	//
	ConnectNotify(CCEV_CONNECTED);	
	SHOW_OBJ_ETIME("CH323Ctrl::OnCallConnect notified");
	return;

CONNECT_ERROR:
	// release all channels
	ReleaseAllChannels();
	InternalDisconnect();
}

// LOOKLOOK methinks ConnectNotify might need to propagate the conference ID.
// This will be a moot point if we have a real property interface.  Watch
// for this in the meantime
VOID CH323Ctrl::ConnectNotify(DWORD dwEvent)		
{
	FX_ENTRY ("CH323Ctrl::ConnectNotify");
	CTRL_USER_INFO UserInfo;
	LPWSTR lpwstr = NULL;
	WCHAR wc =0;

	// init to zero in case of error
	UserInfo.dwCallerIDSize = 0;
	UserInfo.lpvCallerIDData = NULL;
	UserInfo.lpvRemoteProtocolInfo = NULL;	
	UserInfo.lpvLocalProtocolInfo = NULL;

	// alias address strings, e.g. caller ID, are in UNICODE
	if(	m_pRemoteAliasItem &&
		m_pRemoteAliasItem->pData &&
		*((LPWSTR*)(m_pRemoteAliasItem->pData)))
	{
		lpwstr =(LPWSTR)m_pRemoteAliasItem->pData;
	}
	else
	{
		lpwstr = pwszPeerDisplayName;
	}

	if(lpwstr)
	{
		if(pwszPeerAliasName)
		{
			MemFree(pwszPeerAliasName);
		}
		ULONG ulSize = (lstrlenW(lpwstr) + 1) * sizeof(WCHAR);
		pwszPeerAliasName = (LPWSTR)MemAlloc(ulSize);
		LStrCpyW(pwszPeerAliasName, lpwstr);
		// point to user name stuff
		UserInfo.dwCallerIDSize = ulSize;
		UserInfo.lpvCallerIDData = (LPVOID)pwszPeerAliasName;
	}
	else
	{
		// point to the single NULL character on the stack
		UserInfo.dwCallerIDSize = 1;
		UserInfo.lpvCallerIDData = &wc;
	}
	DoAdvise(dwEvent, &UserInfo);
}		


	
VOID CH323Ctrl::NewRemoteUserInfo(PCC_ALIASNAMES pRemoteAliasNames,
		LPWSTR pwszRemotePeerDisplayName)
{
	FX_ENTRY ("CH323Ctrl::NewRemoteUserInfo");
	ULONG ulSize;
	PCC_ALIASITEM pItem;
	WORD wC;
	// make a copy of the user display name (what else???)  We need to hold this
	// at least until the parent object is notified and has a chance to copy the
	// information

	// Future implementation will store each item as a distinct property.
	// These will be accessable via the IProperty interface
	
	// find the display name if it exists
	if(pRemoteAliasNames)
	{
		wC = pRemoteAliasNames->wCount;
		pItem = pRemoteAliasNames->pItems;
		while (wC--)
		{
			if(!pItem)
			{
				continue;
			}
			if(pItem->wType == CC_ALIAS_H323_ID)
			{
				if(!pItem->wDataLength  || !pItem->pData)
				{
					continue;
				}
				
				if(m_pRemoteAliasItem)
				{
					DEBUGMSG(ZONE_CONN,("%s: Releasing previous user info\r\n",_fx_));
					MemFree(m_pRemoteAliasItem);
				}
				// The H323 ID is UNICODE, and needs to be converted to ANSI
				// for propagation to UI/client app.  The conversion is done
				// in ConnectNotify()

				// need enough mem for the struct, the name, + null terminator
				ulSize = ((pItem->wDataLength +1)*sizeof(WCHAR)) + sizeof(CC_ALIASITEM);
				
				m_pRemoteAliasItem = (PCC_ALIASITEM)MemAlloc(ulSize);
				memcpy(m_pRemoteAliasItem, pItem, sizeof(CC_ALIASITEM));	
				m_pRemoteAliasItem->pData = (WCHAR*)(((char *)m_pRemoteAliasItem)+sizeof(CC_ALIASITEM));
				memcpy(m_pRemoteAliasItem->pData, pItem->pData, pItem->wDataLength*sizeof(WCHAR));
				// need to null terminate it
				*(WCHAR *)(((BYTE *)m_pRemoteAliasItem->pData) + pItem->wDataLength*sizeof(WCHAR))
					= (WCHAR)0;
			}
			pItem++;
		}
	}
	if(pwszRemotePeerDisplayName)
	{
		if(pwszPeerDisplayName)
		{
			DEBUGMSG(ZONE_CONN,("%s: Releasing previous pwszPeerDisplayName\r\n",_fx_));
			MemFree(pwszPeerDisplayName);
		}
		// this WAS the Q.931 display name which WAS always ascii
		// ulSize = lstrlen(szRemotePeerDisplayName) + 1;
		// Now it's unicode
		ulSize = (lstrlenW(pwszRemotePeerDisplayName) + 1)* sizeof(WCHAR);
		pwszPeerDisplayName = (LPWSTR)MemAlloc(ulSize);
		memcpy(pwszPeerDisplayName, pwszRemotePeerDisplayName, ulSize);	
	}
}

VOID CH323Ctrl::OnCallRinging(HRESULT hStatus, PCC_RINGING_CALLBACK_PARAMS pRingingParams)
{
	if(pRingingParams->pNonStandardData)
	{

		// nyi
	}
	DoAdvise(CCEV_RINGING, NULL);
}



HRESULT CH323Ctrl::FindDefaultRXChannel(PCC_TERMCAP pChannelCapability, ICtrlCommChan **lplpChannel)
{
	FX_ENTRY ("CH323Ctrl::FindDefaultRXChannel");
	HRESULT hr = hrSuccess;
	GUID mediaID;
	POSITION pos = m_ChannelList.GetHeadPosition();	
	ICtrlCommChan *pChannel;
	if(!pChannelCapability | !lplpChannel)
	{
		ERRORMESSAGE(("%s: null param:pcap:0x%08lX, pchan:0x%08lX\r\n",_fx_,
			pChannelCapability, lplpChannel));
		hr = CCO_E_INVALID_PARAM;
		goto EXIT;
	}

	// look for a matching channel instance.
	while (pos)
	{
		pChannel = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
		ASSERT(pChannel);
		if(pChannel->IsSendChannel() == FALSE)
		{
			hr = pChannel->GetMediaType(&mediaID);
			if(!HR_SUCCEEDED(hr))
				goto EXIT;
			if(((mediaID == MEDIA_TYPE_H323AUDIO) && (pChannelCapability->DataType ==H245_DATA_AUDIO))	
			 	|| ((mediaID == MEDIA_TYPE_H323VIDEO) && (pChannelCapability->DataType ==H245_DATA_VIDEO)))
			{
				*lplpChannel = pChannel;
				return hrSuccess;
			}
		}
	}
	// fallout if not found
	hr = CCO_E_NODEFAULT_CHANNEL;
EXIT:
	return hr;
}	

#ifdef DEBUG
VOID DumpWFX(LPWAVEFORMATEX lpwfxLocal, LPWAVEFORMATEX lpwfxRemote)
{
	FX_ENTRY("DumpWFX");
	ERRORMESSAGE((" -------- %s Begin --------\r\n",_fx_));
	if(lpwfxLocal)
	{
		ERRORMESSAGE((" -------- Local --------\r\n"));
		ERRORMESSAGE(("wFormatTag:\t0x%04X, nChannels:\t0x%04X\r\n",
			lpwfxLocal->wFormatTag, lpwfxLocal->nChannels));
		ERRORMESSAGE(("nSamplesPerSec:\t0x%08lX, nAvgBytesPerSec:\t0x%08lX\r\n",
			lpwfxLocal->nSamplesPerSec, lpwfxLocal->nAvgBytesPerSec));
		ERRORMESSAGE(("nBlockAlign:\t0x%04X, wBitsPerSample:\t0x%04X, cbSize:\t0x%04X\r\n",
			lpwfxLocal->nBlockAlign, lpwfxLocal->wBitsPerSample, lpwfxLocal->cbSize));
	}
	if(lpwfxRemote)
	{
			ERRORMESSAGE((" -------- Remote --------\r\n"));
		ERRORMESSAGE(("wFormatTag:\t0x%04X, nChannels:\t0x%04X\r\n",
			lpwfxRemote->wFormatTag, lpwfxRemote->nChannels));
		ERRORMESSAGE(("nSamplesPerSec:\t0x%08lX, nAvgBytesPerSec:\t0x%08lX\r\n",
			lpwfxRemote->nSamplesPerSec, lpwfxRemote->nAvgBytesPerSec));
		ERRORMESSAGE(("nBlockAlign:\t0x%04X, wBitsPerSample:\t0x%04X, cbSize:\t0x%04X\r\n",
			lpwfxRemote->nBlockAlign, lpwfxRemote->wBitsPerSample, lpwfxRemote->cbSize));
	}
	ERRORMESSAGE((" -------- %s End --------\r\n",_fx_));
}
VOID DumpChannelParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2)
{
	FX_ENTRY("DumpChannelParameters");
	ERRORMESSAGE((" -------- %s Begin --------\r\n",_fx_));
	if(pChanCap1)
	{
		ERRORMESSAGE((" -------- Local Cap --------\r\n"));
		ERRORMESSAGE(("DataType:%d(d), ClientType:%d(d)\r\n",pChanCap1->DataType,pChanCap1->ClientType));
		ERRORMESSAGE(("Direction:%d(d), CapId:%d(d)\r\n",pChanCap1->Dir,pChanCap1->CapId));
	}
	if(pChanCap2)
	{
		ERRORMESSAGE((" -------- Remote Cap --------\r\n"));
		ERRORMESSAGE(("DataType:%d(d), ClientType:%d(d)\r\n",pChanCap2->DataType,pChanCap2->ClientType));
		ERRORMESSAGE(("Direction:%d(d), CapId:%d(d)\r\n",pChanCap2->Dir,pChanCap2->CapId));
	}
	ERRORMESSAGE((" -------- %s End --------\r\n",_fx_));
}
VOID DumpNonstdParameters(PCC_TERMCAP pChanCap1, PCC_TERMCAP pChanCap2)
{
	FX_ENTRY("DumpNonstdParameters");
	
	ERRORMESSAGE((" -------- %s Begin --------\r\n",_fx_));
	DumpChannelParameters(pChanCap1, pChanCap2);
	
	if(pChanCap1)
	{
		ERRORMESSAGE((" -------- Local Cap --------\r\n"));
		if(pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice == h221NonStandard_chosen)
		{
			ERRORMESSAGE(("t35CountryCode:%d(d), t35Extension:%d(d)\r\n",
				pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode,
				pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension));
			ERRORMESSAGE(("MfrCode:%d(d), data length:%d(d)\r\n",
				pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode,
				pChanCap1->Cap.H245Aud_NONSTD.data.length));
		}
		else
		{
			ERRORMESSAGE(("unrecognized nonStandardIdentifier.choice: %d(d)\r\n",
				pChanCap1->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice));
		}
	}
	if(pChanCap2)
	{
		ERRORMESSAGE((" -------- Remote Cap --------\r\n"));
		if(pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice == h221NonStandard_chosen)
		{
			ERRORMESSAGE(("t35CountryCode:%d(d), t35Extension:%d(d)\r\n",
				pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode,
				pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension));
			ERRORMESSAGE(("MfrCode:%d(d), data length:%d(d)\r\n",
				pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode,
				pChanCap2->Cap.H245Aud_NONSTD.data.length));
		}
		else
		{
			ERRORMESSAGE(("nonStandardIdentifier.choice: %d(d)\r\n",
				pChanCap2->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice));
		}
	}
	ERRORMESSAGE((" -------- %s End --------\r\n",_fx_));
}
#else
#define DumpWFX(x,y)
#define DumpChannelParameters(x,y)
#define DumpNonstdParameters(x,y)
#endif

// make sure requested channel parameters are valid (data type, ID and capability
// structure are consistent).  Also obtains the local channel parameters needed
// to deal with the resulting stream
//
BOOL CH323Ctrl::ValidateChannelParameters(PCC_TERMCAP pChanCapLocal, PCC_TERMCAP pChanCapRemote)
{
	FX_ENTRY ("CH323Ctrl::ValidateChannelParameters");
	if((pChanCapLocal->DataType != pChanCapRemote->DataType)
	|| (pChanCapLocal->ClientType != pChanCapRemote->ClientType))
	{
		DEBUGMSG(ZONE_CONN,("%s:unmatched type\r\n",_fx_));
		DumpChannelParameters(pChanCapLocal, pChanCapRemote);
		return FALSE;
	}
	if(pChanCapLocal->ClientType == H245_CLIENT_AUD_NONSTD)
	{
		PNSC_AUDIO_CAPABILITY pNSCapLocal, pNSCapRemote;
		
		if((pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice
			 != pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.choice )
		||(pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode
			!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35CountryCode)
		||(pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension
			!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.t35Extension)
		||(pChanCapLocal->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode
			!= pChanCapRemote->Cap.H245Aud_NONSTD.nonStandardIdentifier.u.h221NonStandard.manufacturerCode)
		||(pChanCapLocal->Cap.H245Aud_NONSTD.data.length
			!= pChanCapRemote->Cap.H245Aud_NONSTD.data.length))
		{
			DEBUGMSG(ZONE_CONN,("%s:unmatched NonStd capability\r\n",_fx_));
			DumpNonstdParameters(pChanCapLocal, pChanCapRemote);
			return FALSE;
		}

		//
		pNSCapLocal = (PNSC_AUDIO_CAPABILITY)pChanCapLocal->Cap.H245Aud_NONSTD.data.value;
		pNSCapRemote = (PNSC_AUDIO_CAPABILITY)pChanCapRemote->Cap.H245Aud_NONSTD.data.value;

		// we only know about NSC_ACM_WAVEFORMATEX at this time
		if(pNSCapRemote->cap_type != NSC_ACM_WAVEFORMATEX)
		{
			DEBUGMSG(ZONE_CONN,("%s:unrecognized NonStd capability type %d\r\n",_fx_, pNSCapRemote->cap_type));
			return FALSE;
		}
		if((pNSCapLocal->cap_data.wfx.cbSize != pNSCapRemote->cap_data.wfx.cbSize)
		|| (memcmp(&pNSCapLocal->cap_data.wfx, &pNSCapRemote->cap_data.wfx, sizeof(WAVEFORMATEX)) != 0))
		{
			DumpWFX(&pNSCapLocal->cap_data.wfx, &pNSCapRemote->cap_data.wfx);
			return FALSE;
		}
		
	}
	else
	{
		
	}
	// if it falls out, it's valid
	return TRUE;

}


BOOL CH323Ctrl::ConfigureRecvChannelCapability(
	ICtrlCommChan *pChannel,
	PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS  pChannelParams)
{
	FX_ENTRY ("CH323Ctrl::ConfigureRecvChannelCapability");
	//IH323PubCap *pCapObject = NULL;
	CapsCtl *pCapObject = NULL;
	// CCapability *pCapObject = NULL;			
	DWORD dwFormatID =INVALID_AUDIO_FORMAT;
	PCC_TERMCAP pChannelCapability = pChannelParams->pChannelCapability, pSaveChannelCapability = NULL;
	UINT uSize, uLocalParamSize;
	LPVOID lpvData;
	LPVOID pLocalParams;			
	
	DEBUGMSG(ZONE_CONN,("%s: requested capability ID:0x%08lX, dir %d, type %d\r\n",
		_fx_, pChannelCapability->CapId, pChannelCapability->Dir,
			pChannelCapability->DataType));
			

	// at one time, we thought the capability ID would be valid
	// and we would be receiving the format specified in pChannelCapability->CapId
	// but it IS NOT VALID.   The only viable info is in the channel parameters.
	// The  code would be --->>>  dwFormatID = pChannelCapability->CapId;

	// the ID *should* be all that is necessary to configure ourselves.
	// However.....
	
	// validate media (data) type - why? shouldn't this be prevalidated?
	// shouldn't this be eventually used to select a channel object from
	// among multiple channel objects?
	if((pChannelCapability->DataType != H245_DATA_AUDIO) && (pChannelCapability->DataType != H245_DATA_VIDEO))
	{
		hrLast = CCO_E_UNSUPPORTED_MEDIA_TYPE;
		DumpChannelParameters(NULL, pChannelCapability);
		goto BAD_CAPABILITY_EXIT;
	}

  	// Look at the local capability referenced by pChannelCapability->CapId
 	// and Validate the format details

 	hrLast = m_pConfAdvise->GetCapResolver((LPVOID *)&pCapObject, OID_CAP_ACM_TO_H323);
 	if(!HR_SUCCEEDED(hrLast) || (pCapObject == NULL))
	{
		ERRORMESSAGE(("%s: null resolver\r\n",_fx_));
		goto BAD_CAPABILITY_EXIT;
	}
	
	// Find the local *receive* capability that matches the remote *send* channel
	// parameters and get the local parameters.

	uLocalParamSize = pCapObject->GetLocalRecvParamSize(pChannelCapability);
	pLocalParams=MemAlloc (uLocalParamSize);
	if (pLocalParams == NULL)
	{
	   hrLast = CCO_E_SYSTEM_ERROR;
	   goto BAD_CAPABILITY_EXIT;
	}
	hrLast = ((CapsCtl *)pCapObject)->GetDecodeParams( pChannelParams,
		(MEDIA_FORMAT_ID *)&dwFormatID, pLocalParams, uLocalParamSize);

 	if(!HR_SUCCEEDED(hrLast) || (dwFormatID == INVALID_AUDIO_FORMAT))
	{
		ERRORMESSAGE(("%s:GetDecodeParams returned 0x%08lx\r\n",_fx_, hrLast));
		goto BAD_CAPABILITY_EXIT;
	}

	
	// create a marshalled version of channel parameters and store it in the channel for later
	// reference
	if(pChannelCapability->ClientType == H245_CLIENT_AUD_NONSTD)
	{
		// The nonstandard capability already passed all the recognition tests so
		// don't need to test again.
		// Make a flat copy of the nonstandard capability
		uSize = pChannelCapability->Cap.H245Aud_NONSTD.data.length;
		// lpData = pChannelCapability->Cap.H245Aud_NONSTD.data.value;

		pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) +  uSize);
		if(!pSaveChannelCapability)
		{
			hrLast = CCO_E_SYSTEM_ERROR;
			goto BAD_CAPABILITY_EXIT;
		}	
		// copy fixed part
		memcpy(pSaveChannelCapability, pChannelCapability, sizeof(CC_TERMCAP));
		// variable part follows the fixed part
		pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value	
			= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
		// copy variable part
		memcpy(pSaveChannelCapability->Cap.H245Aud_NONSTD.data.value,
			pChannelCapability->Cap.H245Aud_NONSTD.data.value,
			pChannelCapability->Cap.H245Aud_NONSTD.data.length);
		// and length
		pSaveChannelCapability->Cap.H245Aud_NONSTD.data.length
			= pChannelCapability->Cap.H245Aud_NONSTD.data.length;
		
		// make the channel remember the channel parameters.
		// a zero size as the second arg means that a preallocated chunk is being passed
		hrLast = pChannel->ConfigureCapability(pSaveChannelCapability, 0,
			pLocalParams, uLocalParamSize);	
		if(!HR_SUCCEEDED(hrLast))
		{
			ERRORMESSAGE(("%s:ConfigureCapability (recv) returned 0x%08lx\r\n",_fx_, hrLast));
			goto BAD_CAPABILITY_EXIT;
		}
		pSaveChannelCapability=NULL;  // the channel owns this memory now
	}
	else if(pChannelCapability->ClientType == H245_CLIENT_VID_NONSTD)
	{
		// The nonstandard capability already passed all the recognition tests so
		// don't need to test again.
		// Make a flat copy of the nonstandard capability
		uSize = pChannelCapability->Cap.H245Vid_NONSTD.data.length;
		// lpData = pChannelCapability->Cap.H245Vid_NONSTD.data.value;

		pSaveChannelCapability = (PCC_TERMCAP)MemAlloc(sizeof(CC_TERMCAP) +  uSize);
		if(!pSaveChannelCapability)
		{
			hrLast = CCO_E_SYSTEM_ERROR;
			goto BAD_CAPABILITY_EXIT;
		}	
		// copy fixed part
		memcpy(pSaveChannelCapability, pChannelCapability, sizeof(CC_TERMCAP));
		// variable part follows the fixed part
		pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value	
			= (unsigned char *)(((BYTE *)pSaveChannelCapability) + sizeof(CC_TERMCAP));
		// copy variable part
		memcpy(pSaveChannelCapability->Cap.H245Vid_NONSTD.data.value,
			pChannelCapability->Cap.H245Vid_NONSTD.data.value,
			pChannelCapability->Cap.H245Vid_NONSTD.data.length);
		// and length
		pSaveChannelCapability->Cap.H245Vid_NONSTD.data.length
			= pChannelCapability->Cap.H245Vid_NONSTD.data.length;
		
		// make the channel remember the channel parameters.
		// a zero size as the second arg means that a preallocated chunk is being passed
		hrLast = pChannel->ConfigureCapability(pSaveChannelCapability, 0,
			pLocalParams, uLocalParamSize);	
		if(!HR_SUCCEEDED(hrLast))
		{
			ERRORMESSAGE(("%s:ConfigureCapability (recv) returned 0x%08lx\r\n",_fx_, hrLast));
			goto BAD_CAPABILITY_EXIT;
		}
		pSaveChannelCapability=NULL;  // the channel owns this memory now
	}
	else
	{
		// only need to remember the already-flat H.245 cap structure.
		hrLast = pChannel->ConfigureCapability(pChannelCapability, sizeof(CC_TERMCAP),
			pLocalParams, uLocalParamSize);	
		if(!HR_SUCCEEDED(hrLast))
		{
			ERRORMESSAGE(("%s:ConfigureCapability(recv) returned 0x%08lx\r\n",_fx_, hrLast));
			goto BAD_CAPABILITY_EXIT;
		}
	}
	// Remember the receive format ID
	pChannel->SetNegotiatedLocalFormat(dwFormatID);
	
	// very special case check for video Temporal/Spatial tradeoff capability.
	// Set the property of the channel accordingly
	if(pChannelCapability->DataType == H245_DATA_VIDEO )
	{
		BOOL bTSCap;
		bTSCap = ((PVIDEO_CHANNEL_PARAMETERS)pLocalParams)->TS_Tradeoff;
		pChannel->CtrlChanSetProperty(PROP_REMOTE_TS_CAPABLE,&bTSCap, sizeof(bTSCap));
		// don't bother checking or panicking over this SetProperty error
	}
	return TRUE;

///////////////////
BAD_CAPABILITY_EXIT:
	ERRORMESSAGE(("%s:received bad capability\r\n",_fx_));
	hrLast = CCO_E_INVALID_CAPABILITY;
	if(pSaveChannelCapability)
		MemFree(pSaveChannelCapability);
	return FALSE;
}

//
// we're being requested to open a channel for receive
//
VOID CH323Ctrl::OnChannelRequest(HRESULT hStatus,
	PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS pChannelReqParams)
{
	FX_ENTRY("CH323Ctrl::OnChannelRequest");
	
	CC_ADDR CChannelAddr, DChannelAddr;
	PCC_ADDR pCChannelAddr = pChannelReqParams->pPeerRTCPAddr;;
	PCC_TERMCAP				pChannelCapability;
	PSOCKADDR_IN	pAddr;
	SOCKADDR_IN sinC;
	pChannelCapability = pChannelReqParams->pChannelCapability;
	DWORD dwRejectReason = H245_REJ;
	ICtrlCommChan *pChannel;	
		
	if(!pChannelCapability)
	{
		ERRORMESSAGE(("OnChannelRequest: null capability\r\n"));
		goto REJECT_CHANNEL;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest");

//
	// Try to find a default channel to handle this open request.
	hrLast = FindDefaultRXChannel(pChannelCapability, &pChannel);	
	if(!HR_SUCCEEDED(hrLast) || !pChannel)
	{
		// Non-default channels Not Yet Implemented!!!!
		// Ask the parent conference object	to create another channel of the
		// specified media type.  The H.245 media type should map to one of the
		// media type GUIDs that the parent conference object understands.
		// 		GUID typeGuid;
		//		if(!MapGuidType(pChannelCapability, &typeGUID))
		//			goto REJECT_CHANNEL;
		// 		hrLast = m_pConfAdvise->GetChannel(&typeGuid, &pChannel);
		//  	if(!HR_SUCCEEDED(hrLast))
		//			goto REJECT_CHANNEL;
		if(hrLast == CCO_E_NODEFAULT_CHANNEL)
			dwRejectReason = H245_REJ_TYPE_NOTAVAIL;

		goto REJECT_CHANNEL;
	}
	
	if(pChannel->GetHChannel())
	{
		ERRORMESSAGE(("%s: existing channel or leak:0x%08lX\r\n",_fx_,
			pChannel->GetHChannel()));
		goto REJECT_CHANNEL;
	}

	//
	//   test if we want to allow this !!!
	//
	if(!pChannel->IsChannelEnabled())
	{
		goto REJECT_CHANNEL;
	}

	pChannel->SetHChannel(pChannelReqParams->hChannel);
	
	// configure based on the requested capability. (store capability ID, validate requested
	// capabilities
	if(!ConfigureRecvChannelCapability(pChannel, pChannelReqParams))
	{
		goto REJECT_CHANNEL;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest done configuring");

	// select our receive ports for this RTP session
	
	if(!pChannel->SelectPorts((LPIControlChannel)this))
	{
		ERRORMESSAGE(("%s, SelectPorts failed\r\n",_fx_));
		hrLast = CCO_E_BAD_ADDRESS;
		goto REJECT_CHANNEL;
	}

	if(pCChannelAddr)
	{
		if(pCChannelAddr->nAddrType != CC_IP_BINARY)
		{
			ERRORMESSAGE(("%s:invalid address type %d\r\n",_fx_, pCChannelAddr->nAddrType));
			hrLast = CCO_E_BAD_ADDRESS;
			goto REJECT_CHANNEL;
		}
		// pass the remote RTCP address to the channel instance
		sinC.sin_family = AF_INET;
		sinC.sin_addr.S_un.S_addr = htonl(pCChannelAddr->Addr.IP_Binary.dwAddr);
		sinC.sin_port = htons(pCChannelAddr->Addr.IP_Binary.wPort);

		DEBUGMSG(ZONE_CONN,("%s, request reverse port 0x%04x, address 0x%08lX\r\n",_fx_,
			pCChannelAddr->Addr.IP_Binary.wPort,pCChannelAddr->Addr.IP_Binary.dwAddr));
	
		hrLast = pChannel->AcceptRemoteRTCPAddress(&sinC);
		if(hrLast != CC_OK)
		{
			ERRORMESSAGE(("%s, AcceptRemoteRTCPAddress returned 0x%08lX\r\n",_fx_, hrLast));
			goto ERROR_EXIT;
		}
	}
	
	// get the address and ports of our end of the channel
	pAddr = pChannel->GetLocalAddress();
	// fixup channel addr pair structure.
	DChannelAddr.nAddrType = CC_IP_BINARY;
	DChannelAddr.bMulticast = FALSE;
	DChannelAddr.Addr.IP_Binary.wPort = pChannel->GetLocalRTPPort();
	DChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);

	CChannelAddr.nAddrType = CC_IP_BINARY;
	CChannelAddr.bMulticast = FALSE;
	CChannelAddr.Addr.IP_Binary.wPort = pChannel->GetLocalRTCPPort();
	CChannelAddr.Addr.IP_Binary.dwAddr = ntohl(pAddr->sin_addr.S_un.S_addr);

	DEBUGMSG(ZONE_CONN,("%s: accepting on port 0x%04x, address 0x%08lX\r\n",_fx_,
		DChannelAddr.Addr.IP_Binary.wPort,DChannelAddr.Addr.IP_Binary.dwAddr));

	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest accepting");
			
	hrLast = CC_AcceptChannel(pChannelReqParams->hChannel,&DChannelAddr, &CChannelAddr,
		0 /*  this param is the bitrate that will be used by THIS channel !! */);
	
	if(hrLast != CC_OK)
	{
		ERRORMESSAGE(("%s, CC_AcceptChannel returned 0x%08lX\r\n",_fx_, hrLast));
		goto ERROR_EXIT;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelRequest accepted");
	return;
	
REJECT_CHANNEL:	
	{
	// need private HRESULT! don't overwrite the reason we're rejecting the channel!!	
		HRESULT hr;	
		ERRORMESSAGE(("%s, rejecting channel, Dir:%d, DataType:%d, ClientType:%d, CapId:%d\r\n",
		_fx_, pChannelCapability->Dir, pChannelCapability->DataType,
		pChannelCapability->ClientType, pChannelCapability->CapId));
	
		hr = CC_RejectChannel(pChannelReqParams->hChannel, dwRejectReason);
		if(hr != CC_OK)
		{
			ERRORMESSAGE(("%s, CC_RejectChannel returned 0x%08lX\r\n",_fx_, hr));
		}
	}	
ERROR_EXIT:
	return;
}

VOID CH323Ctrl::OnChannelAcceptComplete(HRESULT hStatus,
	PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS pChannelParams)
{
	FX_ENTRY("CH323Ctrl::OnChannelAcceptComplete");
	ICtrlCommChan *pChannel;	
	if(hStatus != CC_OK)	
	{
		return;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelAcceptComplete");

	pChannel = FindChannel(pChannelParams->hChannel);	
	if(!pChannel)
	{
		ERRORMESSAGE(("OnChannelAcceptComplete: hChannel 0x%08lx not found\r\n", pChannelParams->hChannel));
		return;
	}
	
	hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN);	// the receive side is open	
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelAcceptComplete, open done");
	if(HR_SUCCEEDED(hrLast))
	{
		m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_RX, pChannel, this);			
	}
	//
	//	Check for readiness to notify that all required channels are open
	//
	CheckChannelsReady( );
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelAcceptComplete done");
}

VOID CH323Ctrl::OnChannelOpen(HRESULT hStatus,
	PCC_TX_CHANNEL_OPEN_CALLBACK_PARAMS pChannelParams )
{
	FX_ENTRY("CH323Ctrl::OnChannelOpen");
	PCC_ADDR	pChannelRTPAddr;
	PCC_ADDR	pChannelRTCPAddr;
    SOCKADDR_IN sinC, sinD;

	ICtrlCommChan *pChannel = (ICtrlCommChan *)pChannelParams->dwUserToken;	
	// validate channel token - is this what we think it is?
	if(IsBadWritePtr(pChannel, sizeof(ICtrlCommChan)))
	{
		ERRORMESSAGE(("%s:invalid channel token: 0x%08lx\r\n",_fx_, pChannelParams->dwUserToken));
		return;
	}
	if(pChannel->IsSendChannel() == FALSE)
	{
		ERRORMESSAGE(("%s:not a send channel:token 0x%08lX\r\n",_fx_,
			pChannelParams->dwUserToken));
		return;
	}
#ifdef DEBUG
	POSITION pos = m_ChannelList.GetHeadPosition();	
	ICtrlCommChan *pChan;
	BOOL bValid = FALSE;
	// look for a matching channel instance.
	while (pos)
	{
		pChan = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
		ASSERT(pChan);
		if(pChan == pChannel)
		{
			bValid = TRUE;
			break;
		}
	}
	if(!bValid)
	{
		ERRORMESSAGE(("%s:unrecognized token 0x%08lX\r\n",_fx_,
			pChannelParams->dwUserToken));
		return;
	}
#endif	//DEBUG

	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen");

	if((hStatus != CC_OK) || (!(pChannelRTPAddr = pChannelParams->pPeerRTPAddr))
		|| (!(pChannelRTCPAddr = pChannelParams->pPeerRTCPAddr)))
	{
		ERRORMESSAGE(("%s: hStatus:0x%08lX, address:0x%08lX\r\n",_fx_,
			hStatus, pChannelRTPAddr));
		// LOOKLOOK need to interpret hStatus
		// let the channel know what happened.
		pChannel->OnChannelOpen(CHANNEL_REJECTED);	
		
		// the channel knows what happened, so let it do the worrying.
		return;
	}
	// what's the need for the different address types ????
	if((pChannelRTPAddr->nAddrType != CC_IP_BINARY)
		|| (pChannelRTCPAddr->nAddrType != CC_IP_BINARY))
	{
		ERRORMESSAGE(("%s: invalid address types %d, %d\r\n",_fx_,
				pChannelRTPAddr->nAddrType, pChannelRTCPAddr->nAddrType));
		goto ERROR_EXIT;
	}	
	
	// we now have the remote port info ( in host byte order)
	sinD.sin_family = AF_INET;
	sinD.sin_addr.S_un.S_addr = htonl(pChannelRTPAddr->Addr.IP_Binary.dwAddr);
	sinD.sin_port = htons(pChannelRTPAddr->Addr.IP_Binary.wPort);
	
	sinC.sin_family = AF_INET;
	sinC.sin_addr.S_un.S_addr = htonl(pChannelRTCPAddr->Addr.IP_Binary.dwAddr);
	//  There are two ports, but in RTP, it is implicit
	// that the RTCP control port is the next highest port number
	// sinC.sin_port = htons(ntohs(pChannelAddr->Addr.IP_Binary.wPort) +1);
	sinC.sin_port = htons(pChannelRTCPAddr->Addr.IP_Binary.wPort);

	DEBUGMSG(ZONE_CONN,("%s, opened on port 0x%04x, address 0x%08lX\r\n",_fx_,
		pChannelRTPAddr->Addr.IP_Binary.wPort,pChannelRTPAddr->Addr.IP_Binary.dwAddr));

	hrLast = pChannel->AcceptRemoteAddress(&sinD);
	if(!HR_SUCCEEDED(hrLast))
	{
		ERRORMESSAGE(("OnChannelOpen: AcceptRemoteAddress failed\r\n"));
		goto ERROR_EXIT;
	}
	hrLast = pChannel->AcceptRemoteRTCPAddress(&sinC);
	if(!HR_SUCCEEDED(hrLast))
	{	
		ERRORMESSAGE(("OnChannelOpen: AcceptRemoteRTCPAddress failed\r\n"));
		goto ERROR_EXIT;
	}
	
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen opening");
	hrLast = pChannel->OnChannelOpen(CHANNEL_OPEN);	// the send side is open
	if(!HR_SUCCEEDED(hrLast))
	{
		ERRORMESSAGE(("OnChannelOpen:channel's OnChannelOpen() returned 0x%08lX\r\n", hrLast));
		CloseChannel(pChannel);
		goto ERROR_EXIT;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen open done");
	m_pConfAdvise->OnControlEvent(CCEV_CHANNEL_READY_TX, pChannel, this);	
	//
	//	Check for readiness to notify that all required channels are open
	//
	CheckChannelsReady( );	
	SHOW_OBJ_ETIME("CH323Ctrl::OnChannelOpen done");
	return;
	
ERROR_EXIT:
	// need to cleanup, disconnect, etc.
	m_hCallCompleteCode = CCCI_CHANNEL_OPEN_ERROR;
	// let the parent Conference object know about the imminent disconnect
	DoAdvise(CCEV_CALL_INCOMPLETE, &m_hCallCompleteCode);
	hrLast = CCO_E_MANDATORY_CHAN_OPEN_FAILED;

	InternalDisconnect();
	return;
}
VOID CH323Ctrl::OnRxChannelClose(HRESULT hStatus,
	PCC_RX_CHANNEL_CLOSE_CALLBACK_PARAMS pChannelParams )
{
	FX_ENTRY("CH323Ctrl::OnRxChannelClose");
	PCC_ADDR	pChannelRTPAddr;
	PCC_ADDR	pChannelRTCPAddr;
    SOCKADDR_IN sinC, sinD;

	ICtrlCommChan *pChannel;
	if(hStatus != CC_OK)
	{
		ERRORMESSAGE(("%s: hStatus:0x%08lX\r\n",_fx_,hStatus));
		// LOOKLOOK need to interpret hStatus
	}
	if(!(pChannel = FindChannel(pChannelParams->hChannel)))
	{
		ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
		return;
	}
		
	// validate channel - is this really a receive channel?
	if(pChannel->IsSendChannel() == TRUE)
	{
		ERRORMESSAGE(("%s:not a receive channel:hChannel 0x%08lX\r\n",_fx_,
			pChannelParams->hChannel));
		return;
	}
	pChannel->OnChannelClose(CHANNEL_CLOSED);	
	return;
}


VOID CH323Ctrl::OnTxChannelClose(HRESULT hStatus,
	PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS pChannelParams )
{
	FX_ENTRY("CH323Ctrl::OnTxChannelClose");
	PCC_ADDR	pChannelRTPAddr;
	PCC_ADDR	pChannelRTCPAddr;
    SOCKADDR_IN sinC, sinD;

	ICtrlCommChan *pChannel;
	if(hStatus != CC_OK)
	{
		ERRORMESSAGE(("%s: hStatus:0x%08lX\r\n",_fx_,hStatus));
		// LOOKLOOK need to interpret hStatus
	}
	
	if(!(pChannel = FindChannel(pChannelParams->hChannel)))
	{
		CC_CloseChannelResponse(pChannelParams->hChannel, FALSE);
		ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
		return;
	}
	
	// validate channel - is this really a send channel?
	if(pChannel->IsSendChannel() == FALSE)
	{
		ERRORMESSAGE(("%s:not a send channel:hChannel 0x%08lX\r\n",_fx_,
			pChannelParams->hChannel));
		CC_CloseChannelResponse(pChannelParams->hChannel, FALSE);
		return;
	}
	CC_CloseChannelResponse(pChannelParams->hChannel, TRUE);
	pChannel->OnChannelClose(CHANNEL_CLOSED);	
	return;
}

BOOL CH323Ctrl::OnCallAccept(PCC_LISTEN_CALLBACK_PARAMS pListenCallbackParams)
{
	FX_ENTRY ("CH323Ctrl::OnCallAccept");
	BOOL bRet = FALSE;
	CH323Ctrl *pNewConnection = NULL;
	if(m_Phase != CCS_Listening)
	{
		ERRORMESSAGE(("OnCallAccept: unexpected call, m_Phase = 0x%08lX\r\n", m_Phase));
		goto EXIT;
	}

	if((!pListenCallbackParams->pCalleeAddr)
	    || (pListenCallbackParams->pCalleeAddr->nAddrType != CC_IP_BINARY))
	{
		if(pListenCallbackParams->pCalleeAddr)
		{
			ERRORMESSAGE(("%s: invalid address type %d\r\n",_fx_,pListenCallbackParams->pCalleeAddr->nAddrType));
		}
		else
		{
			ERRORMESSAGE(("%s: null local address\r\n",_fx_));
		}

	
		ERRORMESSAGE(("OnCallAccept:where's the local address????\r\n"));
					PHOSTENT phe;
					PSOCKADDR_IN psin;
				 	char szTemp[200];
					LPCSTR lpHostName;		
					gethostname(szTemp,sizeof(szTemp));
			    	lpHostName = szTemp;
					psin = &local_sin;
					phe = gethostbyname(lpHostName);
					if (phe != NULL)
					{
				   		memcpy((char FAR *)&(psin->sin_addr), phe->h_addr,phe->h_length);
						psin->sin_family = AF_INET;
					}
	
	
	}
	else
	{
		// remember our local address
		local_sin.sin_family = AF_INET;
		// in host byte order
		local_sin.sin_addr.S_un.S_addr = htonl(pListenCallbackParams->pCalleeAddr->Addr.IP_Binary.dwAddr);
		// in host byte order
		local_sin.sin_port = htons(pListenCallbackParams->pCalleeAddr->Addr.IP_Binary.wPort);
	}

	
	hrLast = m_pConfAdvise->GetAcceptingObject((LPIControlChannel *)&pNewConnection,
		&m_PID);
	if(HR_SUCCEEDED(hrLast) && pNewConnection)
	{
		// NOTE: The UI does not yet know this new object exists, and we may
		// need to silently delete it if there is a disconnect or error
		// Its ref count is 1 at this point.  The decision to delete could be
		// made inside pNewConnection->AcceptConnection(), (because sometimes
		// socket reads complete synchronously depending on timing) SO, we need to
		// protect the "unwind path" via AddRef() and Release() around the call
		//
		pNewConnection->AddRef();	//
		hrLast = pNewConnection->AcceptConnection(this, pListenCallbackParams);
		pNewConnection->Release();
		if(HR_SUCCEEDED(hrLast))
		{
			// The Intel Call control DLL already did a socket accept, the
			// Accept() methods simply initialize the handles and states of
			// pNewConnection and get user information (caller ID)
			// BUGBUG - the caller ID may change in Intel's code - it might
			// come via a conference event
			DEBUGMSG(ZONE_CONN,("OnCallAccept:accepted on connection 0x%08lX\r\n",pNewConnection));
			bRet = TRUE;						
		}
		else
		{
			ERRORMESSAGE(("OnCallAccept:Accept failed\r\n"));
			// LOOK -  Q: where does the accepting object get cleaned up?
			// A: pNewConnection->AcceptConnection((LPIControlChannel)this)
			// must call pNewConnection->DoAdvise(CCEV_ACCEPT_INCOMPLETE, NULL)
			// if the error occurred before the conference object got involved,
			// and must call InternalDisconnect() if the error occurred after
			// the conference object got involved,
		}

	}
	else
	{
		ERRORMESSAGE(("OnCallAccept:GetAcceptingObject failed, hr=0x%08lx\r\n",hrLast));
	}
	
	EXIT:
	return bRet;		
}	


HRESULT CH323Ctrl::NewConference()
{
	FX_ENTRY ("CH323Ctrl::NewConference");
	CapsCtl *pCapObject = NULL;
	PCC_TERMCAPLIST pTermCaps = NULL;
	CC_OCTETSTRING TerminalID;
	PCC_TERMCAPDESCRIPTORS pCapsList = NULL;
	LPWSTR lpwUserDisplayName;

	hrLast = m_pConfAdvise->GetCapResolver((LPVOID *)&pCapObject, OID_CAP_ACM_TO_H323);
	if(!HR_SUCCEEDED(hrLast) || (pCapObject == NULL))
	{
		ERRORMESSAGE(("%s: null resolver\r\n",_fx_));
		goto EXIT;
	}
	if(m_hConference)
	{
		ERRORMESSAGE(("%s:leak or uninitialized m_hConference:0x%08lx\r\n",_fx_,
				m_hConference));
	}
	
	hrLast = pCapObject->CreateCapList(&pTermCaps, &pCapsList);
	if(!HR_SUCCEEDED(hrLast))
	{
		goto EXIT;
	}
	
	lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
	if(lpwUserDisplayName)
	{
		TerminalID.pOctetString = (BYTE *)lpwUserDisplayName;
		TerminalID.wOctetStringLength = (WORD)lstrlenW(lpwUserDisplayName)*sizeof(WCHAR);
	}
	
	// create a conference
	hrLast = CC_CreateConference(&m_hConference, NULL,
		0,			// DWORD dwConferenceConfiguration,
		pTermCaps,		// PCC_TERMCAPLIST	
		pCapsList,		// ptr to term cap descriptors (PCC_TERMCAPDESCRIPTORS)
		&m_VendorInfo, 		// PVENDORINFO
		(lpwUserDisplayName)? &TerminalID: NULL, 	// PCC_OCTETSTRING pTerminalID,
		(DWORD_PTR)this,
		NULL, 	// CC_TERMCAP_CONSTRUCTOR TermCapConstructor,
		NULL, 	// CC_SESSIONTABLE_CONSTRUCTOR	SessionTableConstructor,		
		CCConferenceCallback);

	if(hrLast != CC_OK)
	{
		ERRORMESSAGE(("%s: CreateConference returned 0x%08lX\r\n", _fx_, hrLast));
	
	}

EXIT:	
	pCapObject->DeleteCapList(pTermCaps, pCapsList);
	return hrLast;

}

HRESULT CH323Ctrl::AcceptConnection(LPIControlChannel pIListenCtrlChan,
    LPVOID lpvListenCallbackParams)
{
	FX_ENTRY ("CH323Ctrl::AcceptConnection");
	BOOL bRet = FALSE;
	CREQ_RESPONSETYPE Response;
	DWORD dwCode = CCR_LOCAL_SYSTEM_ERROR;	// error variable only used in error case
	ULONG ulNameSize, ulSize;
	PSOCKADDR_IN psin;
	LPWSTR lpwUserDisplayName;
	CH323Ctrl *pListenConnection = (CH323Ctrl *)pIListenCtrlChan;	
	P_APP_CALL_SETUP_DATA pAppData = NULL;
	APP_CALL_SETUP_DATA AppData;
	
	PCC_NONSTANDARDDATA	pNSData = ((PCC_LISTEN_CALLBACK_PARAMS)
	    lpvListenCallbackParams)->pNonStandardData;
	
	if(pNSData
	    && pNSData->bCountryCode == USA_H221_COUNTRY_CODE
        // why be this picky -> && pNSData->bExtension == USA_H221_COUNTRY_EXTENSION;
        && pNSData->wManufacturerCode == MICROSOFT_H_221_MFG_CODE
        && pNSData->sData.pOctetString
        && pNSData->sData.wOctetStringLength >= sizeof(MSFT_NONSTANDARD_DATA))
	{
        if(((PMSFT_NONSTANDARD_DATA)pNSData->sData.pOctetString)->
            data_type == NSTD_APPLICATION_DATA)
        {
            AppData.lpData = &((PMSFT_NONSTANDARD_DATA)pNSData->sData.pOctetString)->
                nonstd_data.AppData.data;
            AppData.dwDataSize = (DWORD)
                ((PMSFT_NONSTANDARD_DATA)pNSData->sData.pOctetString)->dw_nonstd_data_size;
            pAppData = &AppData;
        }
    }

    SetRemoteVendorID(((PCC_LISTEN_CALLBACK_PARAMS)lpvListenCallbackParams)->pVendorInfo);
	
	// this object is assuming everything from the listening object, including
	// the "Listening" state

	// enter critical section and make sure another thread is not handling a caller disconnect
	// or timeout
	// EnterCriticalSection()
	if(m_Phase == CCS_Idle)
	{
    	GoNextPhase(CCS_Listening);
		// once in this state, a disconnect on another thread will change the state
		// to something besides CCS_Listening

		pListenConnection->GetLocalAddress(&psin);
		SetLocalAddress(psin);
		
		// steal the conference ID from the listen object
		// m_ConferenceID = pListenConnection->GetConfID();
		memcpy(&m_ConferenceID, pListenConnection->GetConfIDptr(),sizeof(m_ConferenceID));
		ZeroMemory(pListenConnection->GetConfIDptr(),sizeof(m_ConferenceID));

		m_hCall = pListenConnection->GetHCall();

		// steal the user info from the listen object
		m_pRemoteAliasItem = pListenConnection->m_pRemoteAliasItem;
		pListenConnection->m_pRemoteAliasItem = NULL;	// make the listen object forget this

		// steal the peer display name
		pwszPeerDisplayName = pListenConnection->pwszPeerDisplayName;
		pListenConnection->pwszPeerDisplayName = NULL;
		
		lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
	}
	
	// else	already timing out
	// LeaveCriticalSection()

	if (m_Phase != CCS_Listening)	// cleanup before it gets accepted
	{
		goto ACCEPT_ERROR;
	}
	
	// let the conference object know that caller ID info is available
	ConnectNotify(CCEV_CALLER_ID);	

	// Now going to do stuff that might put cleanup responsibility on the
	// conference object or UI. (i.e. the call could be accepted)

	// EnterCriticalSection()
	if(m_Phase == CCS_Listening)
	{	
		// state is still OK
    	GoNextPhase(CCS_Filtering);
		// once in this state, a disconnect on another thread will change the state
		// to something besides CCS_Filtering
	}
	// else	already timing out
	// LeaveCriticalSection()

    if (m_Phase != CCS_Filtering)	// one last chance to cleanup before it gets accepted
	{
		goto ERROR_REJECT;
	}

	// can't (should not) do this inside a critical section
	// create a conference to accept the call
	hrLast = NewConference();
	if(!HR_SUCCEEDED(hrLast))
	{
		ERRORMESSAGE(("%s, NewConference returned 0x%08lX\r\n", _fx_, hrLast));
		goto ERROR_REJECT;
	}

	m_pConfAdvise->AddRef();
	Response = m_pConfAdvise->FilterConnectionRequest(this, pAppData);
	m_pConfAdvise->Release();
	
	// Now it may be in the hands of the Conference object, and the accepted connection will
	// need to go through the "disconnecting" state if cleanup is needed.
	// Because connection code is reentrant, the connection could also have
	// been torn down (via connection methods) while inside
	// m_pConfAdvise->FilterConnectionRequest();
	// In each case below, check validity of the connection state - it might have changed
	// because a connection method was called or because the caller timed out

	switch(Response)
	{	
		default:
		case CRR_ACCEPT:
			if(m_Phase != CCS_Filtering)
			{
				ERRORMESSAGE(("%s, accepting state no longer valid 0x%08lX\r\n", _fx_, hrLast));
				goto CANCEL_ACCEPT;
			}
								
			// accept this request
			hrLast = CC_AcceptCall(m_hConference,
				NULL, 	// PCC_NONSTANDARDDATA	pNonStandardData
				lpwUserDisplayName,
				m_hCall,
				0, 		// DWORD dwBandwidth,
				(DWORD_PTR)this);
				
			if(hrLast != CC_OK)
			{
    	    	m_ChanFlags &= ~CTRLF_OPEN;
				goto CANCEL_ACCEPT;			
			}
				
			GoNextPhase(CCS_Accepting);
			bRet = TRUE;

		break;	
		case CRR_ASYNC:
			if(m_Phase == CCS_Accepting)
			{
				// then call has already been accepted inside FilterConnectionRequest callback
				bRet = TRUE;
			}
			else
			{
				if(m_Phase != CCS_Filtering)
				{
					ERRORMESSAGE(("%s, accepting state no longer valid 0x%08lX\r\n", _fx_, hrLast));
					goto CANCEL_ACCEPT;
				}
				GoNextPhase(CCS_Ringing);
				bRet = TRUE;
			}
		
		break;
		case CRR_BUSY:
			hrLast = CC_RejectCall(CC_REJECT_USER_BUSY,
				NULL, // PCC_NONSTANDARDDATA pNonStandardData
				m_hCall);
			// always clean up this object that's not accepting the call
			GoNextPhase(CCS_Idle);
			goto ACCEPT_ERROR;			
		break;
		case CRR_REJECT:
			hrLast = CC_RejectCall(CC_REJECT_DESTINATION_REJECTION,
				NULL, // PCC_NONSTANDARDDATA pNonStandardData
				m_hCall);
			// always clean up this object that's not accepting the call
			GoNextPhase(CCS_Idle);
			goto ACCEPT_ERROR;	
		break;
		case CRR_SECURITY_DENIED:
			hrLast = CC_RejectCall(CC_REJECT_SECURITY_DENIED,
				NULL, // PCC_NONSTANDARDDATA pNonStandardData
				m_hCall);
			// always clean up this object that's not accepting the call
			GoNextPhase(CCS_Idle);
			goto ACCEPT_ERROR;			
		break;
	}

	return hrLast;		
ERROR_REJECT:
	hrLast = CC_RejectCall(CC_REJECT_UNDEFINED_REASON,
		NULL, // PCC_NONSTANDARDDATA pNonStandardData
		m_hCall);	// always clean up this object that's not accepting the call
	GoNextPhase(CCS_Idle);
			
ACCEPT_ERROR:
	
	DoAdvise(CCEV_ACCEPT_INCOMPLETE, &dwCode);
	return hrLast;	

CANCEL_ACCEPT:
	// InternalDisconnect() can be called from any state, and will do fine if
	// it is already in a disconnecting state.
	InternalDisconnect();
	return hrLast;														
}


VOID CH323Ctrl::Cleanup()
{	
	POSITION pos = m_ChannelList.GetHeadPosition();
	ICtrlCommChan *pChan = NULL;
	
	CleanupConferenceAttributes();
	if(m_hConference)
	{
		hrLast = CC_DestroyConference(m_hConference, FALSE);
		// LOOKLOOK - need to check return code!!!
		m_hConference = 0;
	}

	// reset each channel (cleanup underlying socket references)
	while (pos)
	{
		pChan = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
		ASSERT(pChan);
		// cleanup RTP sockets
		pChan->Reset();
	}
	// clear "socket(s) are open flags
	m_ChanFlags &= ~CTRLF_OPEN;
}

HRESULT CH323Ctrl::GetLocalPort(PORT * lpPort)
{
	*lpPort = ntohs(local_sin.sin_port);
	return hrSuccess;	
}
HRESULT CH323Ctrl::GetRemotePort(PORT * lpPort)
{
	*lpPort = ntohs(remote_sin.sin_port);
	return hrSuccess;	
}

HRESULT CH323Ctrl::GetLocalAddress(PSOCKADDR_IN *lplpAddr)
{
	*lplpAddr = &local_sin;
	return hrSuccess;
}

HRESULT CH323Ctrl::GetRemoteAddress(PSOCKADDR_IN *lplpAddr)
{
	*lplpAddr = &remote_sin;
	return hrSuccess;
}
		
HRESULT CH323Ctrl::ListenOn(PORT Port)
{
	FX_ENTRY ("CH323Ctrl::ListenOn");	
	PCC_ALIASNAMES pAliasNames = m_pConfAdvise->GetUserAliases();
	// temporary hack to override UI's ignorance of multiple protocol types
	if(Port != H323_PORT)
	{
		ERRORMESSAGE(("%s, overriding port %d(d) with H323 port %d\r\n",_fx_,
			Port, H323_PORT));
		Port = H323_PORT;
	}

	// do we need to remember this?
	local_sin.sin_addr.S_un.S_addr =	INADDR_ANY;
	local_sin.sin_family = AF_INET;
	local_sin.sin_port = htons((u_short)Port); // set port
	
	CC_ADDR		ListenAddr;
	
	ListenAddr.nAddrType = CC_IP_BINARY;
	ListenAddr.bMulticast = FALSE;
	// in host byte order
	ListenAddr.Addr.IP_Binary.wPort = (u_short)Port;
	ListenAddr.Addr.IP_Binary.dwAddr = ntohl(INADDR_ANY);

	hrLast = CC_CallListen(&m_hListen, &ListenAddr,
		pAliasNames, (DWORD_PTR)this, CCListenCallback);

	if(hrLast != CC_OK)
	{
		ERRORMESSAGE(("CH323Ctrl::ListenOn:CallListen returned 0x%08lX\r\n", hrLast));
		goto EXIT;
	}	
	

	GoNextPhase(CCS_Listening);
	m_ChanFlags = CTRLF_RESET;
	hrLast = hrSuccess;
EXIT:
	return hrLast;
}		
HRESULT CH323Ctrl::StopListen(VOID)
{
	if(m_Phase == CCS_Listening)
	{
		hrLast = CC_CancelListen(m_hListen);
	}
	else
	{
		hrLast = CCO_E_NOT_LISTENING;
	}

//EXIT:
	return hrLast;
}


HRESULT  CH323Ctrl::AsyncAcceptRejectCall(CREQ_RESPONSETYPE Response)
{
	FX_ENTRY ("CH323Ctrl::AsyncAcceptRejectCall");
	HRESULT hr = CCO_E_CONNECT_FAILED;	
	LPWSTR lpwUserDisplayName;
	
	if(Response == CRR_ACCEPT)
	{	
		DEBUGMSG(ZONE_CONN,("%s:accepting\r\n",_fx_));
		lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
		// check call setup phase - send ready if user's acceptance is what
		// was holding us up
		if((m_Phase == CCS_Ringing) || (m_Phase == CCS_Filtering))
		{
			// accept this request
			hrLast = CC_AcceptCall(m_hConference,
				NULL, 	// PCC_NONSTANDARDDATA	pNonStandardData
				lpwUserDisplayName,
				m_hCall,
				0, 		// DWORD dwBandwidth,
				(DWORD_PTR)this);
						
			if(hrLast != CC_OK)
			{
				ERRORMESSAGE(("%s, CC_AcceptCall() returned 0x%08lX\r\n",_fx_, hrLast));
				goto EXIT;
			}
			GoNextPhase(CCS_Accepting);
			hr = hrSuccess;
		}
	}
	else
	{
		// reject only if in accepting state(s)
		// deletion is possible while in advise callback, so protect w/ AddRef()
		AddRef();
		DEBUGMSG(ZONE_CONN,("%s:rejecting\r\n",_fx_));

		if((m_Phase == CCS_Ringing) || (m_Phase == CCS_Filtering))
		{
			hrLast = CC_RejectCall((Response == CRR_BUSY) ?	
				CC_REJECT_USER_BUSY : CC_REJECT_DESTINATION_REJECTION,
				NULL, // PCC_NONSTANDARDDATA pNonStandardData
				m_hCall);
			if(hrLast != CC_OK)
			{
				ERRORMESSAGE(("%s, CC_RejectCall() returned 0x%08lX\r\n",_fx_, hrLast));
			}
			GoNextPhase(CCS_Idle);
			// notify the UI or application code or whatever..
			DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
		}
		else
		{
			hr = CCO_E_INVALID_PARAM;	// LOOKLOOK - need INVALID_STATE error code
		}
			
		Release();
	}
EXIT:
	return (hr);
}


ULONG CH323Ctrl ::AddRef()
{
	FX_ENTRY ("CH323Ctrl::AddRef");
	uRef++;
	DEBUGMSG(ZONE_REFCOUNT,("%s:(0x%08lX)->AddRef() uRef = 0x%08lX\r\n",_fx_, this, uRef ));
	return uRef;
}

ULONG CH323Ctrl ::Release()
{
	FX_ENTRY("CH323Ctrl ::Release");
	uRef--;
	if(uRef == 0)
	{
		DEBUGMSG(ZONE_CONN,("%s:(0x%08lX)->Releasing in phase:%d\r\n",_fx_, this, m_Phase ));

		if(m_Phase != CCS_Idle)
		{
			ERRORMESSAGE(("CMSIACtrl::uRef zero in non idle (%d) state!\r\n",m_Phase));
			InternalDisconnect();
		}
		delete this;
		return 0;
	}
	DEBUGMSG(ZONE_REFCOUNT,("%s:(0x%08lX)->Release() uRef = 0x%08lX\r\n",_fx_, this, uRef ));
	return uRef;
}



// implement IControlChannel::Disconnect().  Map reason codes to the protocol.
VOID CH323Ctrl::Disconnect(DWORD dwReason)
{
	// no way to propagate reason through H.323 stack?????
	InternalDisconnect();
}

VOID CH323Ctrl::InternalDisconnect()
{
	FX_ENTRY ("CH323Ctrl::Disconnect");
	SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect");
	
	m_ChanFlags &= ~CTRLF_ORIGINATING;	// reset "originating" flag.
	
	DEBUGMSG(ZONE_CONN,("%s, called in state %d, uRef = 0x%08lX\r\n",_fx_, m_Phase, uRef));
	switch(m_Phase)
	{
		case CCS_Connecting:
		case CCS_Accepting:
			// if we believe the control channel is still connected, disconnect
			if(IsCtlChanOpen(m_ChanFlags))
			{
				//set state to indicate disconnecting.
				GoNextPhase(CCS_Disconnecting);
				DEBUGMSG(ZONE_CONN,("%s, Expecting a CC_HANGUP_INDICATION\r\n",_fx_));
				hrLast = CC_Hangup(m_hConference, FALSE, (DWORD_PTR)this);
				if(hrLast != CC_OK)
				{
					ERRORMESSAGE(("%s:Hangup() returned 0x%08lX\r\n",_fx_, hrLast));
				}
				SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect hangup done");
			}
			else
			{
				CC_CancelCall(m_hCall);
				GoNextPhase(CCS_Idle);	// no need to ck retval - we're disconnected
				// notify the UI or application code or whatever..
				DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
			}
		break;
		case CCS_Ringing:
			// The call has not yet been accepted!!! Reject it!
			hrLast = CC_RejectCall(CC_REJECT_UNDEFINED_REASON,
				NULL, // PCC_NONSTANDARDDATA pNonStandardData
				m_hCall);
			SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect reject done");
		
			GoNextPhase(CCS_Idle);
			// notify the UI or application code or whatever..
			DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
		break;
		case CCS_Idle:
		case CCS_Disconnecting:
			ERRORMESSAGE(("%s:called in unconnected state %d\r\n",_fx_, m_Phase));
		break;
		default:
			//CCS_Ringing
			//CCS_Opening
			//CCS_Closing
			//CCS_Ready
			//CCS_InUse
			//CCS_Listening

			// if we believe the control channel is still connected, disconnect
			if(IsCtlChanOpen(m_ChanFlags))
			{
				//set state to indicate disconnecting.
				GoNextPhase(CCS_Disconnecting);
				hrLast = CC_Hangup(m_hConference, FALSE, (DWORD_PTR)this);
				if(hrLast != CC_OK)
				{
					ERRORMESSAGE(("%s:Hangup() returned 0x%08lX\r\n",_fx_, hrLast));
					DoAdvise(CCEV_DISCONNECTED ,NULL);
				}
				SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect hangup done");
			}
			else
			{
				GoNextPhase(CCS_Idle);	// no need to ck retval - we're disconnected
				// notify the UI or application code or whatever..
				DoAdvise(CCEV_DISCONNECTED, &m_hCallCompleteCode);
			}
		break;
	}
	SHOW_OBJ_ETIME("CH323Ctrl::InternalDisconnect done");
}



// 	start the asynchronous stuff that will instantiate a control channel
HRESULT CH323Ctrl::PlaceCall (BOOL bUseGKResolution, PSOCKADDR_IN pCallAddr,		
        P_H323ALIASLIST pDestinationAliases, P_H323ALIASLIST pExtraAliases,  	
	    LPCWSTR pCalledPartyNumber, P_APP_CALL_SETUP_DATA pAppData)
{
	FX_ENTRY ("CH323Ctrl::PlaceCall");	
	CC_ALIASNAMES pstn_alias;
	PCC_ALIASITEM pPSTNAlias = NULL;
	PCC_ALIASNAMES pRemoteAliasNames = NULL;
	PCC_ALIASNAMES pTranslatedAliasNames = NULL;
	PCC_ALIASNAMES pLocalAliasNames = NULL;
	PCC_ADDR pDestinationAddr = NULL;
	PCC_ADDR pConnectAddr = NULL;
	LPWSTR lpwUserDisplayName = m_pConfAdvise->GetUserDisplayName();
    PCC_NONSTANDARDDATA		pNSData = NULL;
    PMSFT_NONSTANDARD_DATA lpNonstdContent = NULL;
	int iLen;
	LPWSTR lpwszDest;
	HRESULT hResult = hrSuccess;
	// validate current state, don't allow bad actions
	if(m_Phase != CCS_Idle)
	{
		hResult = CCO_E_NOT_IDLE;
		goto EXIT;
	}

	OBJ_CPT_RESET;	// reset elapsed timer

	m_ChanFlags |= CTRLF_INIT_ORIGINATING;
	if(!pCallAddr)
	{
		hResult =  CCO_E_BAD_ADDRESS;
		goto EXIT;
	}
	else
	{
		// keep a copy of the address
		SetRemoteAddress(pCallAddr);
	}
	// temporary hack to override UI's ignorance of multiple protocol types
	if(remote_sin.sin_port != htons(H323_PORT))
	{
		ERRORMESSAGE(("%s, overriding port %d(d) with H323 port %d\r\n",_fx_,
			ntohs(remote_sin.sin_port), H323_PORT));
		remote_sin.sin_port = htons(H323_PORT);
	}

	// check for connecting to self (not supported)
	if(local_sin.sin_addr.s_addr == remote_sin.sin_addr.s_addr)
	{
		hResult =  CCO_E_BAD_ADDRESS;
		goto EXIT;
	}

	if(m_pRemoteAliasItem)
	{
		MemFree(m_pRemoteAliasItem);
		m_pRemoteAliasItem = NULL;
	}

	// Is this a PSTN or H.320 gateway call?
	if(pCalledPartyNumber)
	{
		// Then, due to the bogus way that CC_PlaceCall() is overloaded, the remote alias names
		// must be overridden with the E.164 phone number.  The hack is buried in
		// Q931ConnectCallback() in CALLCONT.DLL (thank you Intel).  That hack propagates
		// the phone number to the "CalledPartyNumber" of the SETUP message only if there is
		// exactly one alias, and that one alias is of type E.164.
		
		// get # of characters
		iLen = lstrlenW(pCalledPartyNumber);
		// need buffer of size CC_ALIASITEM plus the size (in bytes) of the string
		pPSTNAlias = (PCC_ALIASITEM)MemAlloc(sizeof(CC_ALIASITEM)
			+ sizeof(WCHAR)* (iLen+1));
		if(!pPSTNAlias)
		{
	        ERRORMESSAGE(("%s:failed alloc of pPSTNAlias:0x%08lx\r\n",_fx_));
			hResult = CCO_E_OUT_OF_MEMORY;
			goto EXIT;
		}
		
		WORD wIndex, wLength =1;  // init wLength to count the null terminator
		WCHAR E164Chars[] = {CC_ALIAS_H323_PHONE_CHARS};
		LPCWSTR lpSrc = pCalledPartyNumber;
		pPSTNAlias->wType = CC_ALIAS_H323_PHONE;
		// set offsets - the E.164 address (a phone number) is the only thing
		// in the alias name buffer
		lpwszDest = (LPWSTR)(((char *)pPSTNAlias)+ sizeof(CC_ALIASITEM));
		pPSTNAlias->pData = lpwszDest;
		while(iLen--)
		{
			wIndex = (sizeof(E164Chars)/sizeof (WCHAR)) -1;	//scan E164Chars[]
			do
			{
				if(*lpSrc == E164Chars[wIndex])
				{
					*lpwszDest++ = *lpSrc;
					wLength++;
					break;
				}
			}while(wIndex--);
			
			lpSrc++;
		}
		// terminate it
		*lpwszDest = 0;
		
		// wDataLength is the # of UNICODE characters
		pPSTNAlias->wDataLength = wLength;
		pstn_alias.wCount = 1;
		pstn_alias.pItems = pPSTNAlias;
		pRemoteAliasNames = &pstn_alias;
			
	}
	else if (pDestinationAliases && bUseGKResolution)// use the supplied callee alias names
	{
		hrLast = AllocTranslatedAliasList(&pTranslatedAliasNames, pDestinationAliases);
		if(!HR_SUCCEEDED(hrLast))
		{
			ERRORMESSAGE(("%s, AllocTranslatedAliasList returned 0x%08lX\r\n", _fx_, hrLast));
			hResult = CCO_E_SYSTEM_ERROR;
			goto EXIT;
		}
		pRemoteAliasNames = pTranslatedAliasNames;
	}
	// else pRemoteAliasNames is initialized to NULL
	

	pLocalAliasNames = m_pConfAdvise->GetUserAliases();
	// start!!!
	CC_ADDR ConfAddr;
	// fixup the intel version of the address
	// also note that it's all in host byte order
	ConfAddr.bMulticast = FALSE;
	ConfAddr.nAddrType = CC_IP_BINARY;
	//hrLast = GetRemotePort(&ConfAddr.Addr.IP_Binary.wPort);
	ConfAddr.Addr.IP_Binary.wPort = htons(remote_sin.sin_port);
	ConfAddr.Addr.IP_Binary.dwAddr = ntohl(remote_sin.sin_addr.S_un.S_addr);
	
	#ifdef DEBUG	
		if(m_hConference)
			ERRORMESSAGE(("%s:leak or uninitialized m_hConference:0x%08lx\r\n",_fx_,
				m_hConference));
	#endif  // DEBUG
	
	// create a conference to place the call
	SHOW_OBJ_ETIME("PlaceCall ready to create conference");
	hrLast = NewConference();
	if(!HR_SUCCEEDED(hrLast))
	{
		ERRORMESSAGE(("%s, NewConference returned 0x%08lX\r\n", _fx_, hrLast));
		hResult = CCO_E_SYSTEM_ERROR;
		goto EXIT;
	}


	// Set connect timeout value
	// LOOKLOOK - this is a hardcoded value - !!!  Where should this actualy come from?
	// 30 secs == 30000mS
	SHOW_OBJ_ETIME("PlaceCall setting timeout");

	hrLast = CC_SetCallControlTimeout(CC_Q931_ALERTING_TIMEOUT, 30000);
										
    if(pAppData)
    {
        // typical case - app data should be really small
        if(pAppData->dwDataSize <= APPLICATION_DATA_DEFAULT_SIZE)
        {
            m_NonstdContent.data_type = NSTD_APPLICATION_DATA;
            m_NonstdContent.dw_nonstd_data_size = pAppData->dwDataSize;
            memcpy(&m_NonstdContent.nonstd_data.AppData.data,
                pAppData->lpData, pAppData->dwDataSize);
        	m_NonstandardData.sData.pOctetString  = (LPBYTE) &m_NonstdContent;
        	m_NonstandardData.sData.wOctetStringLength  = sizeof(m_NonstdContent);
        }
        else // need some heap
        {
            UINT uTotalSize = sizeof(MSFT_NONSTANDARD_DATA)+ pAppData->dwDataSize;
            lpNonstdContent = (PMSFT_NONSTANDARD_DATA)MemAlloc(uTotalSize);
            if(lpNonstdContent)
            {
                lpNonstdContent->data_type = NSTD_APPLICATION_DATA;
                lpNonstdContent->dw_nonstd_data_size = pAppData->dwDataSize;
                memcpy(&lpNonstdContent->nonstd_data.AppData.data, pAppData->lpData,pAppData->dwDataSize);
        	    m_NonstandardData.sData.pOctetString  = (LPBYTE) lpNonstdContent;
            	m_NonstandardData.sData.wOctetStringLength  = LOWORD(uTotalSize);
            }
            else
            {
                ERRORMESSAGE(("%s, alloc failed\r\n", _fx_));
        		hResult = CCO_E_SYSTEM_ERROR;
        		goto EXIT;
            }
        }
        pNSData = &m_NonstandardData;
    }

	m_NonstandardData.bCountryCode = USA_H221_COUNTRY_CODE;
    m_NonstandardData.bExtension = USA_H221_COUNTRY_EXTENSION;
    m_NonstandardData.wManufacturerCode = MICROSOFT_H_221_MFG_CODE;

	SHOW_OBJ_ETIME("CH323Ctrl::PlaceCall ready to place call");

	// set destination address pointers
	if(bUseGKResolution)
	{
		// the address passed in pCallAddr is the GK's address
		pConnectAddr = &ConfAddr;
	}
	else
	{
		pDestinationAddr = &ConfAddr;
	}
	hrLast = CC_PlaceCall(
		m_hConference,
		&m_hCall,
		pLocalAliasNames, 	// 	PCC_ALIASNAMES			pLocalAliasNames,
		pRemoteAliasNames,
		NULL, 				// PCC_ALIASNAMES			pExtraCalleeAliasNames,
		NULL, 				// PCC_ALIASITEM			pCalleeExtension,
		pNSData,	        // PCC_NONSTANDARDDATA		pNonStandardData,
		lpwUserDisplayName, // PWSTR pszDisplay,
		pDestinationAddr, 	//  Destination call signalling address
		pConnectAddr, 		// 	address to send the SETUP message to, if different than
		 			// 	the destination address.  (used for gatekeeper calls?)
		0, 			//	DWORD                   dwBandwidth,
		(DWORD_PTR) this);

	SHOW_OBJ_ETIME("CH323Ctrl::PlaceCall placed call");

	//  clear these out so that cleanup does not try to free later
	if(lpNonstdContent)
   	    MemFree(lpNonstdContent);
	m_NonstandardData.sData.pOctetString  = NULL;
	m_NonstandardData.sData.wOctetStringLength = 0;

	// check return from CC_PlaceCall
	if(hrLast != CC_OK)
	{
		ERRORMESSAGE(("CH323Ctrl::PlaceCall, PlaceCall returned 0x%08lX\r\n", hrLast));
		hResult = CCO_E_CONNECT_FAILED;	
		goto EXIT;
	}
	// wait for an indication
	GoNextPhase(CCS_Connecting);

	EXIT:	
	if(pTranslatedAliasNames)
	{
		FreeTranslatedAliasList(pTranslatedAliasNames);
	}
	if(pPSTNAlias)
	{
	  MemFree(pPSTNAlias);
	}
	return hResult;
}

//
//	Given HCHANNEL, find the channel object.
//

ICtrlCommChan *CH323Ctrl::FindChannel(CC_HCHANNEL hChannel)
{
	FX_ENTRY ("CH323Ctrl::FindChannel");	
	// find the channel

	POSITION pos = m_ChannelList.GetHeadPosition();
	ICtrlCommChan *pChannel;
	while (pos)
	{
		pChannel = (ICtrlCommChan *) m_ChannelList.GetNext(pos);
		ASSERT(pChannel);
		if(pChannel->GetHChannel() == hChannel)
			return pChannel;
	}

	#ifdef DEBUG
	// fallout to error case
	ERRORMESSAGE(("%s, did not find hChannel 0x%08lX\r\n",_fx_,hChannel));
	#endif // DEBUG
	
	return NULL;
}

VOID  CH323Ctrl::OnMute(HRESULT hStatus,
				PCC_MUTE_CALLBACK_PARAMS pParams)
{
	FX_ENTRY ("CH323Ctrl::OnMute");	
	ICtrlCommChan *pChannel;
	HRESULT hr;
	if(!(pChannel = FindChannel(pParams->hChannel)))
	{
	    ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
        return;
	}
	hr = pChannel->PauseNet(TRUE, TRUE);
	if(!HR_SUCCEEDED(hr))
	{
        ERRORMESSAGE(("%s, Pausenet returned 0x%08lx\r\n", _fx_, hr));
	}
}
VOID  CH323Ctrl::OnUnMute(HRESULT hStatus,
				PCC_UNMUTE_CALLBACK_PARAMS pParams)
{
	FX_ENTRY ("CH323Ctrl::OnUnMute");	
	ICtrlCommChan *pChannel;
	HRESULT hr;

	if(!(pChannel = FindChannel(pParams->hChannel)))
	{
	    ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
        return;
	}
	hr = pChannel->PauseNet(FALSE, TRUE);
	if(!HR_SUCCEEDED(hr))
	{
        ERRORMESSAGE(("%s, Pausenet returned 0x%08lx\r\n", _fx_, hr));
	}
}


VOID  CH323Ctrl::OnMiscCommand(HRESULT hStatus,
				PCC_H245_MISCELLANEOUS_COMMAND_CALLBACK_PARAMS pParams)
{
	FX_ENTRY ("CH323Ctrl::OnMiscCommand");	
	ICtrlCommChan *pChannel;

	// not every command references an individual channel. The 4 exceptions are:
	// case equaliseDelay_chosen:		
	// case zeroDelay_chosen:
	// case multipointModeCommand_chosen:
	// case cnclMltpntMdCmmnd_chosen:
	//
	// if we were betting on receiving few of the exceptional cases, we would always
	// try to find the channel.
	//if(!(pChannel = FindChannel(pParams->hChannel)))
	//{
	//	ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
		// but don't error because of the exceptions
	//}
	
	switch(pParams->pMiscellaneousCommand->type.choice)
 	{
		// the name and spelling of these constants was invented by the OSS compiler
		//
		case videoFreezePicture_chosen:
			if(!(pChannel = FindChannel(pParams->hChannel)))
			{
				ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
				break;
			}
	
		break;
		case videoFastUpdatePicture_chosen:		// the receiver wants an I-Frame
		{
			HRESULT hr;
			IVideoChannel *pIVC=NULL;
			if(!(pChannel = FindChannel(pParams->hChannel)))
			{
				ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
				break;
			}
			hr = pChannel->QueryInterface(IID_IVideoChannel, (void **)&pIVC);
			if(HR_SUCCEEDED(hr))
			{
				pIVC->SendKeyFrame();
				pIVC->Release();
			}
			// else it must not be a video channel
			
		}
		break;
		case MCd_tp_vdTmprlSptlTrdOff_chosen:	
		{
			DWORD dwTradeoff;
			HRESULT hr;
			if(!(pChannel = FindChannel(pParams->hChannel)))
			{
				ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
				break;
			}
			// set TS value of the channel, also propagate to Datapump
			dwTradeoff  = MAKELONG(
				pParams->pMiscellaneousCommand->type.u.MCd_tp_vdTmprlSptlTrdOff, 0);
			// set channel property
			// NOTE: when PROP_TS_TRADEOFF is set, the channel does all the
			// local tweaking to make it happen. The channel will also signal the
			// new value to the remote as if the local end initiated it.
			hr = pChannel->CtrlChanSetProperty(PROP_TS_TRADEOFF, &dwTradeoff, sizeof(dwTradeoff));
		}
		break;
		
		default:
		// the following are not currently handled
		//	case equaliseDelay_chosen:		
		//	case zeroDelay_chosen:
		//	case videoSendSyncEveryGOB_chosen:
		//	case vdSndSyncEvryGOBCncl_chosen:
		//	case videoFastUpdateGOB_chosen:		// suposedly required by H.323
		//	case videoFastUpdateMB_chosen:		// suposedly required by H.323

		// and the remaining 2 are handled by the call control layer
		// so we will never see these
		//		case multipointModeCommand_chosen:	
		//		case cnclMltpntMdCmmnd_chosen:

		break;

	}

}
VOID  CH323Ctrl::OnMiscIndication(HRESULT hStatus,
				PCC_H245_MISCELLANEOUS_INDICATION_CALLBACK_PARAMS pParams)
{
	FX_ENTRY ("CH323Ctrl::OnMiscIndication");	
	ICtrlCommChan *pChannel;
	HRESULT hr;
	unsigned short choice = pParams->pMiscellaneousIndication->type.choice;
	
	if(!(pChannel = FindChannel(pParams->hChannel)))
	{
		ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
	    // check the exceptional cases for which this is OK
	    if((choice == multipointConference_chosen)
	        || (choice == cnclMltpntCnfrnc_chosen)
	        || (choice == multipointZeroComm_chosen)
	        || (choice == cancelMultipointZeroComm_chosen)
	        || (choice == mltpntScndryStts_chosen)
	        || (choice == cnclMltpntScndryStts_chosen))
	    {
            return;     // as long as the above choices are not supported......
	    }

	}
	switch(choice)
	{
    	case logicalChannelActive_chosen:
		    hr = pChannel->PauseNet(FALSE, TRUE);
		break;
		case logicalChannelInactive_chosen:
		    hr = pChannel->PauseNet(TRUE, TRUE);
		break;

		case MIn_tp_vdTmprlSptlTrdOff_chosen:
		{
			HRESULT hr;
			DWORD dwTradeoff = MAKELONG(0,
				pParams->pMiscellaneousIndication->type.u.MIn_tp_vdTmprlSptlTrdOff);

			if(!(pChannel = FindChannel(pParams->hChannel)))
			{
				ERRORMESSAGE(("%s, channel not found\r\n", _fx_));
				break;
			}
			// Set the indicated TS value of the channel.
			// This should never occur for send channels.
			//
			hr = pChannel->CtrlChanSetProperty(PROP_TS_TRADEOFF_IND, &dwTradeoff, sizeof(dwTradeoff));
		}
		break;

		// the following are not currently handled
		//	case multipointConference_chosen:
		//	case cnclMltpntCnfrnc_chosen:
		//	case multipointZeroComm_chosen:
		//	case cancelMultipointZeroComm_chosen:
		//	case mltpntScndryStts_chosen:
		//	case cnclMltpntScndryStts_chosen:
		//	case vdIndctRdyTActvt_chosen:
		//	case videoNotDecodedMBs_chosen:

	}
}

HRESULT CH323Ctrl::MiscChannelCommand(
	ICtrlCommChan *pChannel,
	VOID * pCmd)
{

#ifdef BETA_2_ASN_PRESENT
    if(m_fAvoidCrashingPDUs)
        return hrSuccess;
#endif // BETA_2_ASN_PRESENT

	return CC_H245MiscellaneousCommand(m_hCall, pChannel->GetHChannel(),
		(MiscellaneousCommand *)pCmd);
}

HRESULT CH323Ctrl::MiscChannelIndication(
	ICtrlCommChan *pChannel,
	VOID * pInd)
{
    MiscellaneousIndication *pMI = (MiscellaneousIndication *)pInd;
            			
#ifdef BETA_2_ASN_PRESENT
    if(m_fAvoidCrashingPDUs)
        return hrSuccess;
#endif

    // Intel decided that they had to wrap two Misc commands with two separate,
    // additional APIs. And it won't allow those to be issued any other way.
    // (it returns an error).  Until we fix that, need to catch and reroute those
    // two special ones
    if(pMI->type.choice  == logicalChannelActive_chosen)
    {
		 return CC_UnMute(pChannel->GetHChannel());
    }
    else if (pMI->type.choice  == logicalChannelInactive_chosen )
    {
        return CC_Mute(pChannel->GetHChannel());
    }
    else
        return CC_H245MiscellaneousIndication(m_hCall,pChannel->GetHChannel(),pMI);
    					
}

VOID CH323Ctrl::SetRemoteVendorID(PCC_VENDORINFO pVendorInfo)
{
    if(!pVendorInfo)
        return;

    m_RemoteVendorInfo.bCountryCode = pVendorInfo->bCountryCode;
    m_RemoteVendorInfo.bExtension = pVendorInfo->bExtension;
    m_RemoteVendorInfo.wManufacturerCode = pVendorInfo->wManufacturerCode;
    if(pVendorInfo->pProductNumber
        && pVendorInfo->pProductNumber->wOctetStringLength
        && pVendorInfo->pProductNumber->pOctetString)
    {
        if(m_RemoteVendorInfo.pProductNumber)
        {
            MemFree(m_RemoteVendorInfo.pProductNumber);
        }
        m_RemoteVendorInfo.pProductNumber = (PCC_OCTETSTRING)
            MemAlloc(sizeof(CC_OCTETSTRING)
            + pVendorInfo->pProductNumber->wOctetStringLength);
        if(m_RemoteVendorInfo.pProductNumber)
        {
            m_RemoteVendorInfo.pProductNumber->wOctetStringLength
              = pVendorInfo->pProductNumber->wOctetStringLength;
            m_RemoteVendorInfo.pProductNumber->pOctetString =
                ((BYTE *)m_RemoteVendorInfo.pProductNumber + sizeof(CC_OCTETSTRING));
            memcpy(m_RemoteVendorInfo.pProductNumber->pOctetString,
                pVendorInfo->pProductNumber->pOctetString,
                pVendorInfo->pProductNumber->wOctetStringLength);
        }

    }
    if(pVendorInfo->pVersionNumber)
    {
        if(m_RemoteVendorInfo.pVersionNumber)
        {
            MemFree(m_RemoteVendorInfo.pVersionNumber);
        }
        m_RemoteVendorInfo.pVersionNumber = (PCC_OCTETSTRING)
            MemAlloc(sizeof(CC_OCTETSTRING)
            + pVendorInfo->pVersionNumber->wOctetStringLength);
        if(m_RemoteVendorInfo.pVersionNumber)
        {
            m_RemoteVendorInfo.pVersionNumber->wOctetStringLength
              = pVendorInfo->pVersionNumber->wOctetStringLength;
            m_RemoteVendorInfo.pVersionNumber->pOctetString =
                ((BYTE *)m_RemoteVendorInfo.pVersionNumber + sizeof(CC_OCTETSTRING));
            memcpy(m_RemoteVendorInfo.pVersionNumber->pOctetString,
                pVendorInfo->pVersionNumber->pOctetString,
                pVendorInfo->pVersionNumber->wOctetStringLength);
        }
    }
#ifdef BETA_2_ASN_PRESENT
    char IntelCrashingID[] = "Intel Internet Video Phone";
    char IntelCrashingVer[] = "1.0";

    m_fAvoidCrashingPDUs = FALSE;  // innocent until proven guilty
    if(m_RemoteVendorInfo.bCountryCode == USA_H221_COUNTRY_CODE)
    {
        // then it's possible that it is Intel or Microsoft
        if(m_RemoteVendorInfo.wManufacturerCode == MICROSOFT_H_221_MFG_CODE)
        {
            if((!pVendorInfo->pProductNumber) && (!pVendorInfo->pVersionNumber))
            {
                // safe to assume this is Beta2 or Beta3
                m_fAvoidCrashingPDUs = TRUE;
            }
            else if((pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->wOctetStringLength == 0)
                && (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->wOctetStringLength == 0))
            {
                // safe to assume this is Beta2 or Beta3
                m_fAvoidCrashingPDUs = TRUE;
            }
        }
        else if(m_RemoteVendorInfo.wManufacturerCode == INTEL_H_221_MFG_CODE)
        {
            if(pVendorInfo->pProductNumber
                && pVendorInfo->pVersionNumber
                && pVendorInfo->pProductNumber->wOctetStringLength
                && pVendorInfo->pProductNumber->pOctetString
                && pVendorInfo->pVersionNumber->wOctetStringLength
                && pVendorInfo->pVersionNumber->pOctetString)

            {
                // compare strings, don't care about null terminator
                if((0 == memcmp(pVendorInfo->pProductNumber->pOctetString,
                    IntelCrashingID, min(sizeof(IntelCrashingID)-1,pVendorInfo->pProductNumber->wOctetStringLength)))
                 && (0 == memcmp(pVendorInfo->pVersionNumber->pOctetString,
                    IntelCrashingVer,
                    min(sizeof(IntelCrashingVer)-1, pVendorInfo->pVersionNumber->wOctetStringLength)) ))
                {
                   m_fAvoidCrashingPDUs = TRUE;
                }
            }
        }
    }
#endif  //BETA_2_ASN_PRESENT



}

HRESULT CH323Ctrl::Init(IConfAdvise *pConfAdvise)
{
	hrLast = hrSuccess;
	
	if(!(m_pConfAdvise = pConfAdvise))
	{
		hrLast = CCO_E_INVALID_PARAM;
		goto EXIT;
	}

EXIT:	
	return hrLast;
}

HRESULT CH323Ctrl::DeInit(IConfAdvise *pConfAdvise)
{
	hrLast = hrSuccess;
	if(m_pConfAdvise != pConfAdvise)
	{
		hrLast = CCO_E_INVALID_PARAM;
		goto EXIT;
	}
	m_pConfAdvise = NULL;

EXIT:	
	return hrLast;
}

BOOL CH323Ctrl::IsAcceptingConference(LPVOID lpvConfID)
{
	if(memcmp(lpvConfID, &m_ConferenceID, sizeof(m_ConferenceID))==0)
	{	
		return TRUE;
	}
	return FALSE;
}

HRESULT CH323Ctrl::GetProtocolID(LPGUID lpPID)
{
	if(!lpPID)
		return CCO_E_INVALID_PARAM;

	*lpPID = m_PID;
	hrLast = hrSuccess;
	return hrLast;
}
	
IH323Endpoint * CH323Ctrl::GetIConnIF()
{
	if(!m_pConfAdvise)
		return NULL;
	return m_pConfAdvise->GetIConnIF();
}	

STDMETHODIMP CH323Ctrl::GetVersionInfo(
        PCC_VENDORINFO *ppLocalVendorInfo,
        PCC_VENDORINFO *ppRemoteVendorInfo)
{

	FX_ENTRY ("CH323Ctrl::GetVersionInfo");
	if(!ppLocalVendorInfo || !ppRemoteVendorInfo)
	{
		return CCO_E_INVALID_PARAM;
	}
	*ppLocalVendorInfo = &m_VendorInfo;
	*ppRemoteVendorInfo = &m_RemoteVendorInfo;
	return hrSuccess;
}



CH323Ctrl::CH323Ctrl()
:m_hListen(0),
m_hConference(0),
m_hCall(0),
m_pRemoteAliases(NULL),
m_pRemoteAliasItem(NULL),
pwszPeerDisplayName(NULL),
pwszPeerAliasName(NULL),
m_bMultipointController(FALSE),
m_fLocalT120Cap(TRUE),
m_fRemoteT120Cap(FALSE),
hrLast(hrSuccess),
m_ChanFlags(0),
m_hCallCompleteCode(0),
m_pConfAdvise(NULL),
m_Phase( CCS_Idle ),
#ifdef BETA_2_ASN_PRESENT
    m_fAvoidCrashingPDUs(FALSE),
#endif

uRef(1)
{
	m_PID = PID_H323;
	ZeroMemory(&m_ConferenceID,sizeof(m_ConferenceID));
	ZeroMemory(&local_sin, sizeof(local_sin));
	ZeroMemory(&remote_sin, sizeof(remote_sin));
	ZeroMemory(&m_RemoteVendorInfo, sizeof(m_RemoteVendorInfo));
	local_sin_len =  sizeof(local_sin);
	remote_sin_len = sizeof(remote_sin);
	
	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));
    }

	m_NonstandardData.bCountryCode = USA_H221_COUNTRY_CODE;
	m_NonstandardData.bExtension =  USA_H221_COUNTRY_EXTENSION;
	m_NonstandardData.wManufacturerCode = MICROSOFT_H_221_MFG_CODE;
	m_NonstandardData.sData.pOctetString  = NULL;
	m_NonstandardData.sData.wOctetStringLength  = 0;
	m_ParticipantList.wLength = 0;
	m_ParticipantList.ParticipantInfoArray = NULL;
	m_ConferenceAttributes.pParticipantList = &m_ParticipantList;
}


VOID CH323Ctrl ::ReleaseAllChannels()
{
	ICtrlCommChan *pChan = NULL;
	if (!m_ChannelList.IsEmpty())
	{
		while (!m_ChannelList.IsEmpty())
		{
			pChan = (ICtrlCommChan *) m_ChannelList.RemoveHead();
			if(pChan)
			{
				pChan->EndControlSession();
				pChan->Release();
				pChan = NULL;
			}
		}
	}
}

CH323Ctrl ::~CH323Ctrl()
{
	Cleanup();
	ReleaseAllChannels();
	if(m_pRemoteAliases)
		FreeTranslatedAliasList(m_pRemoteAliases);
	if(pwszPeerDisplayName)
		MemFree(pwszPeerDisplayName);
	if(pwszPeerAliasName)
		MemFree(pwszPeerAliasName);
	if(m_pRemoteAliasItem)
		MemFree(m_pRemoteAliasItem);
	if(m_NonstandardData.sData.pOctetString)
		MemFree(m_NonstandardData.sData.pOctetString);
    if(m_VendorInfo.pProductNumber)
        MemFree(m_VendorInfo.pProductNumber);
    if(m_VendorInfo.pVersionNumber)
        MemFree(m_VendorInfo.pVersionNumber);
    if(m_RemoteVendorInfo.pProductNumber)
        MemFree(m_RemoteVendorInfo.pProductNumber);
    if(m_RemoteVendorInfo.pVersionNumber)
        MemFree(m_RemoteVendorInfo.pVersionNumber);
}