/****************************************************************************/ // acaapi.cpp // // RDP Control Arbitrator API functions // // Copyright (C) Microsoft, PictureTel 1993-1997 // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "acaapi" #include #include /****************************************************************************/ /* API FUNCTION: CA_Init */ /* */ /* Called to initialize the CA. */ /****************************************************************************/ void RDPCALL SHCLASS CA_Init(void) { DC_BEGIN_FN("CA_Init"); #define DC_INIT_DATA #include #undef DC_INIT_DATA // Set local node initial state to detached. caStates[0] = CA_STATE_DETACHED; DC_END_FN(); } /****************************************************************************/ // CA_ReceivedPacket // // Handles control PDUs inbound from the client. /****************************************************************************/ void RDPCALL SHCLASS CA_ReceivedPacket( PTS_CONTROL_PDU pControlPDU, unsigned DataLength, LOCALPERSONID personID) { DC_BEGIN_FN("CA_ReceivedPacket"); // Ensure we can access the header. if (DataLength >= sizeof(TS_CONTROL_PDU)) { TRC_NRM((TB, "[%u] Packet:%d", personID, pControlPDU->action)); switch (pControlPDU->action) { case TS_CTRLACTION_REQUEST_CONTROL: CAEvent(CA_EVENTI_TRY_GIVE_CONTROL, (UINT32)personID); break; case TS_CTRLACTION_COOPERATE: CAEvent(CA_EVENTI_REMOTE_COOPERATE, (UINT32)personID); break; default: // An invalid action - log and disconnect the Client. TRC_ERR((TB, "Invalid CA msg %d", pControlPDU->action)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_InvalidControlPDUAction, (BYTE *)&(pControlPDU->action), sizeof(pControlPDU->action)); break; } } else { TRC_ERR((TB,"Data length %u too short for control header", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShareDataTooShort, (BYTE *)pControlPDU, DataLength); } DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CA_PartyJoiningShare */ /* */ /* Called when a new party is joining the share */ /* */ /* PARAMETERS: */ /* personID - the ID of the new party. */ /* oldShareSize - the number of the parties which were in the share (ie */ /* excludes the joining party). */ /* */ /* RETURNS: */ /* TRUE - the CA can accept the new party */ /* FALSE - the CA cannot accept the new party */ /****************************************************************************/ BOOL RDPCALL SHCLASS CA_PartyJoiningShare( LOCALPERSONID personID, unsigned oldShareSize) { DC_BEGIN_FN("CA_PartyJoiningShare"); /************************************************************************/ // Check for share start, do some init if need be. /************************************************************************/ if (oldShareSize == 0) { // Enter cooperate mode then viewing. Note that we don't send // a message now (we will send one when we process a CA_SyncNow call). CAEvent(CA_EVENTI_ENTER_COOP_MODE, CA_DONT_SEND_MSG); CAEvent(CA_EVENTI_ENTER_VIEWING_MODE, 0); caWhoHasControlToken = (LOCALPERSONID)-1; } // Set new node state to detached -- we should receive a CA packet // to tell us what the remote state really is before we receive any IM // packets. But just in case, choosing detached is the safest as it has // least priveleges. Note that we will not enable the shadow cursors // until (and if) we get a CA packet to tell us the remote system is // detached. caStates[personID] = CA_STATE_DETACHED; DC_END_FN(); return TRUE; } /****************************************************************************/ /* API FUNCTION: CA_PartyLeftShare */ /* */ /* Called when a party has left the share. */ /* */ /* PARAMETERS: */ /* personID - the local ID of the new party. */ /* newShareSize - the number of the parties now in the share (ie excludes */ /* the leaving party). */ /****************************************************************************/ void RDPCALL SHCLASS CA_PartyLeftShare( LOCALPERSONID personID, unsigned newShareSize) { DC_BEGIN_FN("CA_PartyLeftShare"); // Just ensure their state is left as detached for safety. caStates[personID] = CA_STATE_DETACHED; DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CA_SyncNow */ /* */ /* Called by the TT to signal the beginning of a sync. */ /****************************************************************************/ void RDPCALL SHCLASS CA_SyncNow(void) { DC_BEGIN_FN("CA_SyncNow"); /************************************************************************/ /* Tell the world we're cooperating. */ /************************************************************************/ TRC_NRM((TB, "Send cooperate")); CAFlushAndSendMsg(TS_CTRLACTION_COOPERATE, 0, 0); DC_END_FN(); } /****************************************************************************/ /* FUNCTION: CASendMsg */ /* */ /* Sends a CA message to remote system */ /****************************************************************************/ __inline BOOL RDPCALL SHCLASS CASendMsg( UINT16 msg, UINT16 data1, UINT32 data2) { NTSTATUS status; PTS_CONTROL_PDU pControlPDU; DC_BEGIN_FN("CASendMsg"); status = SC_AllocBuffer((PPVOID)&pControlPDU, sizeof(TS_CONTROL_PDU)); if ( STATUS_SUCCESS == status ) { // Set up the packet header for a request message pControlPDU->shareDataHeader.pduType2 = TS_PDUTYPE2_CONTROL; pControlPDU->action = msg; pControlPDU->grantId = data1; pControlPDU->controlId = data2; SC_SendData((PTS_SHAREDATAHEADER)pControlPDU, sizeof(TS_CONTROL_PDU), sizeof(TS_CONTROL_PDU), PROT_PRIO_MISC, 0); } else { TRC_NRM((TB, "Failed to allocate packet %d", msg)); // Continue periodic scheduling. SCH_ContinueScheduling(SCH_MODE_NORMAL); } DC_END_FN(); return STATUS_SUCCESS == status; } /****************************************************************************/ /* FUNCTION: CAFlushAndSendMsg */ /* */ /* This function will attempt to flush any outstanding CA messages and send */ /* the supplied message. This function relies on the CA messages being */ /* contiguous between TS_CTRLACTION_FIRST and TS_CTRLACTION_LAST. If it */ /* fails to send the message then it will set a flag to remember it is */ /* pending and continue to try to send the message every time it is called. */ /* */ /* PARAMETERS: */ /* msg - the message to be sent. If this is CA_NO_MESSAGE then the */ /* function only attempts to send outstanding messages. */ /* */ /* data1, data2 - the data for the extra fields in the message. */ /* */ /* RETURNS: TRUE or FALSE - whether the supplied message was sent */ /****************************************************************************/ BOOL RDPCALL SHCLASS CAFlushAndSendMsg( UINT16 msg, UINT16 GrantID, UINT32 ControlID) { BOOL rc; int i; DC_BEGIN_FN("CAFlushAndSendMsg"); /************************************************************************/ /* The order messages get sent is not important - merely that they get */ /* sent as soon as possible whilst the conditions which triggered our */ /* intial attempt to send them are still valid (ie if we try to send a */ /* TS_CTRLACTION_COOPERATE we drop a pending TS_CTRLACTION_DETACH if */ /* there is one - see CAEvent). This means we can just ensure that the */ /* pending flag is set for our current event and then try to send all */ /* pending events. The other important point about CA messages is that */ /* the only one which can be sent out repeatedly is a */ /* TS_CTRLACTION_REQUEST_CONTROL. We effectively give that the lowest */ /* priority by looking through our array of pending flags backwards so */ /* that even if it is repeating and we can only ever get one packet */ /* here we will send the other messages (although it is unlikely that */ /* they'll be generated whilst we are requesting control). */ /************************************************************************/ /************************************************************************/ /* Set the pending flag for the current message */ /************************************************************************/ if (msg >= TS_CTRLACTION_FIRST && msg <= TS_CTRLACTION_LAST) { caPendingMessages[msg - TS_CTRLACTION_FIRST].pending = TRUE; caPendingMessages[msg - TS_CTRLACTION_FIRST].grantId = GrantID; caPendingMessages[msg - TS_CTRLACTION_FIRST].controlId = ControlID; } else { TRC_ASSERT((msg == CA_NO_MESSAGE),(TB,"Invalid msg")); } /************************************************************************/ /* Now flush out the pending messages. */ /************************************************************************/ for (i = (TS_CTRLACTION_LAST - TS_CTRLACTION_FIRST); i >= 0; i--) { if (caPendingMessages[i].pending) { // Try to send this message. if (CASendMsg((UINT16)(i + TS_CTRLACTION_FIRST), caPendingMessages[i].grantId, caPendingMessages[i].controlId)) { caPendingMessages[i].pending = FALSE; if (i == (TS_CTRLACTION_GRANTED_CONTROL - TS_CTRLACTION_FIRST)) { // When we successfully send a granted message to another // party then we relinquish control and must issue a // 'given control' event. if (caPendingMessages[i].grantId != SC_GetMyNetworkPersonID()) { CAEvent(CA_EVENTI_GIVEN_CONTROL, (UINT32)SC_NetworkIDToLocalID( caPendingMessages[i].grantId)); } } } else { // It didn't work so break out and don't try to send any // more messages. break; } } } /************************************************************************/ /* Now return the status according to whether we sent the message that */ /* we were called with. */ /************************************************************************/ if (msg != CA_NO_MESSAGE) rc = !caPendingMessages[msg - TS_CTRLACTION_FIRST].pending; else rc = TRUE; DC_END_FN(); return rc; } /****************************************************************************/ /* FUNCTION: CAEvent */ /* */ /* Called from many CA functions to manage various inputs. */ /* */ /* PARAMETERS: */ /* caEvent - the event, the possible events are (the meaning of the */ /* additionalData parameter if any is given in brackets) */ /* */ /* The possible events for CA_Event plus */ /* */ /* CA_EVENTI_REQUEST_CONTROL */ /* CA_EVENTI_TRY_GIVE_CONTROL(person ID of party requesting control) */ /* CA_EVENTI_GIVEN_CONTROL(person ID who we gave control to) */ /* CA_EVENTI_GRANTED_CONTROL(person ID of the party granted control) */ /* CA_EVENTI_ENTER_DETACHED_MODE */ /* CA_EVENTI_ENTER_COOPERATING_MODE */ /* CA_EVENTI_ENTER_CONTROL_MODE */ /* CA_EVENTI_ENTER_VIEWING_MODE */ /* CA_EVENTI_REMOTE_DETACH(person ID of remote party) */ /* CA_EVENTI_REMOTE_COOPERATE(person ID of remote party) */ /* CA_EVENTI_GRAB_CONTROL */ /* */ /* additionalData - depends on the caEvent, see above. */ /****************************************************************************/ void RDPCALL SHCLASS CAEvent( unsigned caEvent, UINT32 additionalData) { LOCALPERSONID i; DC_BEGIN_FN("CAEvent"); TRC_NRM((TB, "Processing event - %d(%04lX)", caEvent, additionalData)); switch (caEvent) { case CA_EVENT_OLD_UNATTENDED: case CA_EVENT_CANT_CONTROL: case CA_EVENT_BEGIN_UNATTENDED: case CA_EVENT_TAKE_CONTROL: case CA_EVENT_DETACH_CONTROL: case CA_EVENTI_REQUEST_CONTROL: case CA_EVENTI_REMOTE_DETACH: case CA_EVENTI_GRAB_CONTROL: case CA_EVENTI_GRANTED_CONTROL: case CA_EVENTI_ENTER_DETACHED_MODE: case CA_EVENTI_ENTER_CONTROL_MODE: /****************************************************************/ /* we don't expect to get any of these */ /****************************************************************/ TRC_ALT((TB, "Nonsensical CA event %d", caEvent)); break; case CA_EVENTI_TRY_GIVE_CONTROL: { NETPERSONID destPersonID; /****************************************************************/ /* always try to give up control */ /****************************************************************/ destPersonID = SC_LocalIDToNetworkID( (LOCALPERSONID)additionalData); /****************************************************************/ /* Give control to the destPersonID. If this fails */ /* CAFlushAndSendMsg will remember for us and retry. */ /****************************************************************/ CAFlushAndSendMsg(TS_CTRLACTION_GRANTED_CONTROL, (UINT16)destPersonID, SC_GetMyNetworkPersonID()); break; } case CA_EVENTI_GIVEN_CONTROL: /****************************************************************/ /* Now update our globals. */ /****************************************************************/ caWhoHasControlToken = (LOCALPERSONID)additionalData; /****************************************************************/ /* make sure we go to viewing */ /****************************************************************/ CAEvent(CA_EVENTI_ENTER_VIEWING_MODE, 0); /****************************************************************/ /* Update the state for the person we gave control to (as we */ /* will not see the GRANTED message). */ /****************************************************************/ i = (LOCALPERSONID)additionalData; if (caStates[i] == CA_STATE_VIEWING) caStates[i] = CA_STATE_IN_CONTROL; break; case CA_EVENTI_ENTER_COOP_MODE: /****************************************************************/ /* Notify the remote system that we are cooperating - forget */ /* about any detach messages we've been unable to send. If */ /* we fail to send the message now then CAFlushAndSendMsg will */ /* remember and retry for us. We will enter cooperating state */ /* anyway. */ /****************************************************************/ caPendingMessages[TS_CTRLACTION_DETACH - TS_CTRLACTION_FIRST]. pending = FALSE; if (additionalData != CA_DONT_SEND_MSG) CAFlushAndSendMsg(TS_CTRLACTION_COOPERATE, 0, 0); break; case CA_EVENTI_ENTER_VIEWING_MODE: // Change to viewing state. caStates[0] = CA_STATE_VIEWING; break; case CA_EVENTI_REMOTE_COOPERATE: TRC_NRM((TB, "Person %d (Local) is cooperating", additionalData)); /****************************************************************/ /* Make the state change */ /****************************************************************/ if (caWhoHasControlToken == (LOCALPERSONID)additionalData) caStates[additionalData] = CA_STATE_IN_CONTROL; else caStates[additionalData] = CA_STATE_VIEWING; break; case CA_EVENT_COOPERATE_CONTROL: TRC_ALT((TB, "Nonsensical CA event %d", caEvent)); #ifdef Unused /****************************************************************/ /* need to change to viewing mode after leaving detached mode */ /****************************************************************/ CAEvent(CA_EVENTI_ENTER_COOP_MODE, 0); /****************************************************************/ /* Enter viewing mode. */ /****************************************************************/ CAEvent(CA_EVENTI_ENTER_VIEWING_MODE, 0); #endif break; default: TRC_ERR((TB, "Unrecognised event - %d", caEvent)); break; } DC_END_FN(); }