/* * 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;wwLength;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;wwLength;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); }