windows-nt/Source/XPSP1/NT/termsrv/remdsk/rds/as/cpi32/s20.cpp
2020-09-26 16:20:57 +08:00

2641 lines
66 KiB
C++

#include "precomp.h"
//
// S20.CPP
// T.128 Protocol
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_NET
//
// S20_Init()
// Initializes the T.128 protocol layer
//
BOOL S20_Init(void)
{
BOOL rc = FALSE;
DebugEntry(S20_Init);
ASSERT(g_s20State == S20_TERM);
//
// Register with the network layer.
//
if (!MG_Register(MGTASK_DCS, &g_s20pmgClient, g_putAS))
{
ERROR_OUT(("Failed to register MG layer"));
DC_QUIT;
}
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(S20_Init, rc);
return(rc);
}
//
// S20_Term()
// This cleans up the T.128 protocol layer.
//
void S20_Term(void)
{
DebugEntry(S20_Term);
//
// Note that this case statement is unusual in that it falls through
// from each condition. This happens to suit the termination
// processing rather well.
//
switch (g_s20State)
{
case S20_IN_SHARE:
case S20_SHARE_PEND:
//
// Notify share ended
//
SC_End();
//
// FALL THROUGH
//
case S20_NO_SHARE:
case S20_JOIN_PEND:
//
// Leave our channels
//
if (g_s20BroadcastID != 0)
{
MG_ChannelLeave(g_s20pmgClient, g_s20BroadcastID);
g_s20BroadcastID = 0;
}
if (g_s20JoinedLocal)
{
MG_ChannelLeave(g_s20pmgClient, g_s20LocalID);
g_s20JoinedLocal = FALSE;
}
//
// FALL THROUGH
//
case S20_ATTACH_PEND:
//
// Detach from the domain.
//
MG_Detach(g_s20pmgClient);
case S20_INIT:
//
// We may end up here with g_s20BroadcastID & g_s20JoinedLocal
// non-zero if Term was called in the middle of a share. Clear these
// variables so when we start back up again via Init, it
// works the same way as first initialization.
//
// Note we do not need to leave the channels.
//
g_s20BroadcastID = 0;
g_s20JoinedLocal = FALSE;
//
// Deregister.
//
MG_Deregister(&g_s20pmgClient);
g_s20State = S20_TERM;
TRACE_OUT(("g_s20State is S20_TERM"));
case S20_TERM:
//
// Finally we break out.
//
break;
default:
ERROR_OUT(("invalid state %d", g_s20State));
break;
}
DebugExitVOID(S20_Term);
}
//
// S20_AllocPkt
// Allocates a SEND packet for either the S20 protocol, syncs, or data
//
PS20DATAPACKET S20_AllocDataPkt
(
UINT streamID,
UINT nodeID, // One person or broadcast
UINT cbSizePacket
)
{
PS20DATAPACKET pS20Packet = NULL;
NET_PRIORITY priority;
BOOL rc = FALSE;
DebugEntry(S20_AllocDataPkt);
ASSERT(g_s20State == S20_IN_SHARE);
//
// Try to send queued control packets first.
//
if (S20SendQueuedControlPackets() != 0)
{
//
// If there are still queued control packets then don't allow any
// allocation.
//
DC_QUIT;
}
priority = S20StreamToS20Priority(streamID);
//
// Note:
// Sends to an individual node are NOT flow-controlled. Only the
// global app sharing channel is.
//
if (MG_GetBuffer(g_s20pmgClient, cbSizePacket, priority,
(NET_CHANNEL_ID)nodeID, (void **)&pS20Packet) != 0)
{
TRACE_OUT(("MG_GetBuffer failed; can't allocate S20 packet"));
}
else
{
pS20Packet->header.packetType = S20_DATA | S20_ALL_VERSIONS;
pS20Packet->header.user = g_s20LocalID;
pS20Packet->correlator = g_s20ShareCorrelator;
pS20Packet->stream = 0;
pS20Packet->dataLength = cbSizePacket - sizeof(S20DATAPACKET) + sizeof(DATAPACKETHEADER);
rc = TRUE;
}
DC_EXIT_POINT:
DebugExitPVOID(S20_AllocDataPkt, pS20Packet);
return(pS20Packet);
}
//
// S20_FreeDataPkt - see s20.h
//
void S20_FreeDataPkt(PS20DATAPACKET pS20Packet)
{
DebugEntry(S20_FreeDataPkt);
MG_FreeBuffer(g_s20pmgClient, (void **)&pS20Packet);
DebugExitVOID(S20_FreeDataPkt);
}
//
// S20_SendDataPkt - see s20.h
//
void S20_SendDataPkt
(
UINT streamID,
UINT nodeID,
PS20DATAPACKET pS20Packet
)
{
UINT rc;
NET_PRIORITY priority;
DebugEntry(S20_SendDataPkt);
priority = S20StreamToS20Priority(streamID);
//
// Note:
// Sends to an individual are not flow-controlled. Only sends to
// everybody on the global app sharing channel are.
//
//
// Try to send queued control packets first.
//
rc = S20SendQueuedControlPackets();
if (rc == 0)
{
//
// Fill in the stream, length and correlator before sending.
//
pS20Packet->stream = (BYTE)streamID;
pS20Packet->correlator = g_s20ShareCorrelator;
//
// dataLength includes the DATAPACKETHEADER part of the S20DATAPACKET
// structure
//
TRACE_OUT(("S20_SendPkt: sending data packet size %d",
pS20Packet->dataLength + sizeof(S20DATAPACKET) - sizeof(DATAPACKETHEADER)));
rc = MG_SendData(g_s20pmgClient, priority, (NET_CHANNEL_ID)nodeID,
pS20Packet->dataLength + sizeof(S20DATAPACKET) - sizeof(DATAPACKETHEADER),
(void **)&pS20Packet);
}
// lonchanc: it is ok for MG_SendData returns 0 and NET_CHANNEL_EMPTY
if (rc == NET_RC_MGC_NOT_CONNECTED)
{
S20LeaveOrEndShare();
}
else
{
if (rc != 0)
{
ERROR_OUT(("SendData rc=%lx - expecting share termination soon", rc));
}
}
DebugExitVOID(S20_SendDataPkt);
}
//
// S20_UTEventProc()
//
BOOL CALLBACK S20_UTEventProc
(
LPVOID userData,
UINT event,
UINT_PTR data1,
UINT_PTR data2
)
{
BOOL processed;
DebugEntry(S20_UTEventProc);
processed = TRUE;
switch (event)
{
case NET_EVENT_USER_ATTACH:
S20AttachConfirm(LOWORD(data1), HIWORD(data1), (UINT)data2);
break;
case NET_EVENT_USER_DETACH:
S20DetachIndication(LOWORD(data1), (UINT)data2);
break;
case NET_EVENT_CHANNEL_JOIN:
S20JoinConfirm((PNET_JOIN_CNF_EVENT)data2);
MG_FreeBuffer(g_s20pmgClient, (void **)&data2);
break;
case NET_EVENT_CHANNEL_LEAVE:
S20LeaveIndication(LOWORD(data1), (UINT)data2);
break;
case NET_EVENT_DATA_RECEIVED:
S20SendIndication((PNET_SEND_IND_EVENT)data2);
break;
case NET_FLOW:
//
// Handle the feedback event.
//
S20Flow((UINT)data1, (UINT)data2);
break;
case CMS_NEW_CALL:
if (g_asSession.scState == SCS_INIT)
{
//
// This happens when (a) a new real call is started
// (b) creating a new share in a call fails, so we want to
// then try to join an existing share.
//
SCCheckForCMCall();
}
break;
case CMS_END_CALL:
if (g_asSession.callID)
{
//
// AS lock protects g_asSession global fields
//
TRACE_OUT(("AS LOCK: CMS_END_CALL"));
UT_Lock(UTLOCK_AS);
g_asSession.gccID = 0;
g_asSession.callID = 0;
UT_Unlock(UTLOCK_AS);
TRACE_OUT(("AS UNLOCK: CMS_END_CALL"));
if (g_asSession.scState > SCS_SHAREENDING)
{
SC_EndShare();
}
DCS_NotifyUI(SH_EVT_APPSHARE_READY, FALSE, 0);
g_s20BroadcastID = 0;
g_s20JoinedLocal = FALSE;
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
}
break;
default:
processed = FALSE;
break;
}
DebugExitBOOL(S20_UTEventProc, processed);
return(processed);
}
//
// FUNCTION: S20AttachUser
//
// DESCRIPTION:
//
// Called when we want to attach this sets up the various parameters for
// MG_Attach, calls it and handles the return codes from NET.
//
// PARAMETERS:
//
// callID - the callID provided by the SC user
//
//
//
const NET_FLOW_CONTROL c_S20FlowControl =
{
// latency
{
S20_LATENCY_TOP_PRIORITY,
S20_LATENCY_HIGH_PRIORITY,
S20_LATENCY_MEDIUM_PRIORITY,
S20_LATENCY_LOW_PRIORITY
},
// stream size
{
S20_SIZE_TOP_PRIORITY,
S20_SIZE_HIGH_PRIORITY,
S20_SIZE_MEDIUM_PRIORITY,
S20_SIZE_LOW_PRIORITY
}
};
//
// S20CreateOrJoinShare()
// Creates a share for the first time or joins an existing one
//
// Normally, creating a share requires
// * registration
// * broadcast of S20_CREATE packet
// * reception of one S20_RESPOND packet
// for the share to be created. However, if we're the top provider, we
// assume it's created without waiting for an S20_RESPOND. If something
// goes wrong later, it will clean itself up anyway. Then that allows us
// to host a conference, share an app, and have it be shared through the
// life of the conference, even if remotes call/hang up repeatedly.
//
BOOL S20CreateOrJoinShare
(
UINT what,
UINT callID
)
{
UINT rc = 0;
BOOL noFlowControl;
NET_FLOW_CONTROL flowControl;
DebugEntry(S20CreateOrJoinShare);
ASSERT((what == S20_CREATE) || (what == S20_JOIN));
switch (g_s20State)
{
case S20_INIT:
//
// Remember what to do when we have attached and joined.
//
g_s20Pend = what;
//
// ATTACH the S20 MCS USER
//
COM_ReadProfInt(DBG_INI_SECTION_NAME, S20_INI_NOFLOWCONTROL,
FALSE, &noFlowControl);
if (noFlowControl)
{
WARNING_OUT(("S20 Flow Control is OFF"));
ZeroMemory(&flowControl, sizeof(flowControl));
}
else
{
// Set up our target latencies and stream sizes
flowControl = c_S20FlowControl;
}
//
// Initiate an attach - the domain equals the callID.
//
rc = MG_Attach(g_s20pmgClient, callID, &flowControl);
if (rc == 0)
{
//
// Make the state change if we succeeded
//
g_s20State = S20_ATTACH_PEND;
TRACE_OUT(("g_s20State is S20_ATTACH_PEND"));
}
else
{
//
// End the share immediately and no state change.
//
WARNING_OUT(("MG_Attach of S20 User failed, rc = %u", rc));
g_s20Pend = 0;
SC_End();
}
break;
case S20_ATTACH_PEND:
case S20_JOIN_PEND:
//
// We just need to set the flag in these cases - we will try
// to create a share when we have attached and joined our
// channel.
//
g_s20Pend = what;
break;
case S20_SHARE_PEND:
//
// If a share is pending but the SC user wants to create
// or join again we let them. Multiple outstanding joins are
// benign and another create will have a new correlator so the
// previous one (and any responses to it) will be obsolete.
//
// NOTE DELIBERATE FALL THROUGH
//
//
case S20_NO_SHARE:
TRACE_OUT(("S20_NO_SHARE"));
//
// Broadcast a S20CREATE packet.
//
if (what == S20_CREATE)
{
g_s20ShareCorrelator = S20NewCorrelator();
TRACE_OUT(("CP CREATE %lu %d", g_s20ShareCorrelator, 0));
rc = S20FlushAndSendControlPacket(what,
g_s20ShareCorrelator,
0,
NET_TOP_PRIORITY);
}
else
{
g_s20ShareCorrelator = 0;
TRACE_OUT(("CP JOIN %lu %d", 0, 0));
rc = S20FlushAndSendControlPacket(what, 0, 0,
NET_TOP_PRIORITY);
}
TRACE_OUT(("S20FlushAndSendControlPacket %u", rc));
if (rc == 0)
{
//
// Switch state.
//
g_s20State = S20_SHARE_PEND;
TRACE_OUT(("g_s20State is S20_SHARE_PEND"));
//
// Assume success right away when creating the share. We'll
// hear back in a bit if there's a problem.
//
if (what == S20_CREATE)
{
// Don't check for top provider, assume success always.
TRACE_OUT(("S20: Creating share, assume success"));
//
// LAURABU -- IF THIS CAUSES PROBLEMS, fall back to
// checking for one person only in call.
//
if (!SC_Start(g_s20LocalID))
{
WARNING_OUT(("S20CreateOrJoin: couldn't start share"));
SC_End();
}
else
{
g_s20State = S20_IN_SHARE;
TRACE_OUT(("g_s20State is S20_IN_SHARE"));
}
}
}
else
{
//
// Something failed so we will just forget about the share.
//
WARNING_OUT(("Failed to create share"));
if (what == S20_CREATE)
{
SC_End();
}
}
break;
default:
ERROR_OUT(("Invalid state %u for %u", g_s20State, what));
}
DebugExitBOOL(S20CreateOrJoinShare, (rc == 0));
return(rc == 0);
}
//
// FUNCTION: S20LeaveOrEndShare
//
// DESCRIPTION:
//
// Handles processing a S20_LeaveShare or a S20_EndShare call.
//
// PARAMETERS:
//
// what - what to do (the protocol packet type corresponding to the
// action).
//
// RETURNS: NONE
//
//
void S20LeaveOrEndShare(void)
{
UINT what;
DebugEntry(S20LeaveOrEndShare);
//
// The share is destroyed whenever the creator leaves. Nobody else
// can end it.
//
if (S20_GET_CREATOR(g_s20ShareCorrelator) == g_s20LocalID)
{
what = S20_END;
}
else
{
what = S20_LEAVE;
}
ASSERT(what == S20_LEAVE || what == S20_END);
switch (g_s20State)
{
case S20_ATTACH_PEND:
case S20_JOIN_PEND:
//
// We just need to reset the pending flags here - no state
// change required.
//
g_s20Pend = 0;
break;
case S20_IN_SHARE:
case S20_SHARE_PEND:
TRACE_OUT(("S20_SHARE_PEND"));
//
// Now try and make and send the appropriate control packet.
//
TRACE_OUT(("CP %u %u %d", what, g_s20ShareCorrelator, 0));
S20FlushSendOrQueueControlPacket(what,
g_s20ShareCorrelator,
0,
NET_TOP_PRIORITY);
//
// Make the SHARE_ENDED callback.
//
SC_End();
break;
default:
WARNING_OUT(("invalid state %d for %d", g_s20State, what));
break;
}
DebugExitVOID(S20LeaveOrEndShare);
}
//
// FUNCTION: S20MakeControlPacket
//
// DESCRIPTION:
//
// Attempts to allocate and construct a S20 control packet.
//
// PARAMETERS:
//
// what - which type of packet
//
// correlator - the correlator to place in the packet
//
// who - the target party (if what is a S20_DELETE) or the originator (if
// what is S20_RESPOND)
//
// ppPacket - where to return a pointer to the packet.
//
// pLength - where to return the length of the packet.
//
// priority - priority of packet to make
//
// RETURNS:
//
//
UINT S20MakeControlPacket
(
UINT what,
UINT correlator,
UINT who,
PS20PACKETHEADER * ppPacket,
LPUINT pcbPacketSize,
UINT priority
)
{
UINT rc;
BOOL fPutNameAndCaps;
UINT cbPacketSize;
UINT personNameLength;
PS20PACKETHEADER pS20Packet = NULL;
LPBYTE pData;
DebugEntry(S20MakeControlPacket);
//
// Assume success
//
rc = 0;
//
// Work out how big the packet needs to be. Start with the fixed
// length then add in capabilities and our name (if they are required).
//
switch (what)
{
case S20_CREATE:
cbPacketSize = sizeof(S20CREATEPACKET) - 1;
fPutNameAndCaps = TRUE;
break;
case S20_JOIN:
cbPacketSize = sizeof(S20JOINPACKET) - 1;
fPutNameAndCaps = TRUE;
break;
case S20_RESPOND:
cbPacketSize = sizeof(S20RESPONDPACKET) - 1;
fPutNameAndCaps = TRUE;
break;
case S20_DELETE:
cbPacketSize = sizeof(S20DELETEPACKET) - 1;
fPutNameAndCaps = FALSE;
break;
case S20_LEAVE:
cbPacketSize = sizeof(S20LEAVEPACKET);
fPutNameAndCaps = FALSE;
break;
case S20_END:
cbPacketSize = sizeof(S20ENDPACKET) - 1;
fPutNameAndCaps = FALSE;
break;
case S20_COLLISION:
cbPacketSize = sizeof(S20COLLISIONPACKET);
fPutNameAndCaps = FALSE;
break;
default:
ERROR_OUT(("BOGUS S20 packet %u", what));
break;
}
if (fPutNameAndCaps)
{
ASSERT(g_asSession.gccID);
ASSERT(g_asSession.cchLocalName);
//
// The name data is always dword aligned (including the NULL)
//
personNameLength = DC_ROUND_UP_4(g_asSession.cchLocalName+1);
cbPacketSize += personNameLength + sizeof(g_cpcLocalCaps);
}
//
// Now try to allocate a buffer for this.
//
rc = MG_GetBuffer( g_s20pmgClient,
cbPacketSize,
(NET_PRIORITY)priority,
g_s20BroadcastID,
(void **)&pS20Packet );
if (rc != 0)
{
TRACE_OUT(("MG_GetBuffer failed; can't send S20 control packet"));
DC_QUIT;
}
pS20Packet->packetType = (TSHR_UINT16)what | S20_ALL_VERSIONS;
pS20Packet->user = g_s20LocalID;
//
// This will point to where we need to stuff the name and/or
// capabilities.
//
pData = NULL;
//
// Now do the packet dependant fields.
//
switch (what)
{
case S20_CREATE:
{
ASSERT(fPutNameAndCaps);
((PS20CREATEPACKET)pS20Packet)->correlator = correlator;
((PS20CREATEPACKET)pS20Packet)->lenName = (TSHR_UINT16)personNameLength;
((PS20CREATEPACKET)pS20Packet)->lenCaps = (TSHR_UINT16)sizeof(g_cpcLocalCaps);
pData = ((PS20CREATEPACKET)pS20Packet)->data;
}
break;
case S20_JOIN:
{
ASSERT(fPutNameAndCaps);
((PS20JOINPACKET)pS20Packet)->lenName = (TSHR_UINT16)personNameLength;
((PS20JOINPACKET)pS20Packet)->lenCaps = (TSHR_UINT16)sizeof(g_cpcLocalCaps);
pData = ((PS20JOINPACKET)pS20Packet)->data;
}
break;
case S20_RESPOND:
{
ASSERT(fPutNameAndCaps);
((PS20RESPONDPACKET)pS20Packet)->correlator = correlator;
((PS20RESPONDPACKET)pS20Packet)->originator = (TSHR_UINT16)who;
((PS20RESPONDPACKET)pS20Packet)->lenName = (TSHR_UINT16)personNameLength;
((PS20RESPONDPACKET)pS20Packet)->lenCaps = (TSHR_UINT16)sizeof(g_cpcLocalCaps);
pData = ((PS20RESPONDPACKET)pS20Packet)->data;
}
break;
case S20_DELETE:
{
ASSERT(!fPutNameAndCaps);
((PS20DELETEPACKET)pS20Packet)->correlator = correlator;
((PS20DELETEPACKET)pS20Packet)->target = (TSHR_UINT16)who;
((PS20DELETEPACKET)pS20Packet)->lenName = 0;
}
break;
case S20_LEAVE:
{
ASSERT(!fPutNameAndCaps);
((PS20LEAVEPACKET)pS20Packet)->correlator = correlator;
}
break;
case S20_END:
{
ASSERT(!fPutNameAndCaps);
((PS20ENDPACKET)pS20Packet)->correlator = correlator;
((PS20ENDPACKET)pS20Packet)->lenName = 0;
}
break;
case S20_COLLISION:
{
ASSERT(!fPutNameAndCaps);
((PS20COLLISIONPACKET)pS20Packet)->correlator = correlator;
}
break;
default:
{
ERROR_OUT(("Invalid type %u", what));
}
break;
}
//
// Now we can copy in the name and capabilities.
//
if (fPutNameAndCaps)
{
lstrcpy((LPSTR)pData, g_asSession.achLocalName);
// The local name is always null-terminated (truncated to fit in 48 bytes inc. null)
pData += personNameLength;
memcpy(pData, &g_cpcLocalCaps, sizeof(g_cpcLocalCaps));
//
// FILL IN GCC-ID HERE; the local caps are shared but the local
// person entity in the share doesn't exist yet.
//
((CPCALLCAPS *)pData)->share.gccID = g_asSession.gccID;
}
//
// Return the packet and length.
//
*ppPacket = pS20Packet;
*pcbPacketSize = cbPacketSize;
DC_EXIT_POINT:
DebugExitDWORD(S20MakeControlPacket, rc);
return(rc);
}
//
// FUNCTION: S20FlushSendOrQueueControlPacket
//
// DESCRIPTION:
//
// Attempts to flush any queued S20 control packets and then attempte to
// send a S20 control packet. If this fails the queue the packet (the
// actual packet is freed.
//
// PARAMETERS:
//
// what - which type of packet
//
// correlator - the correlator to place in the packet
//
// who - the target party (if what is a S20_DELETE) or the originator (if
// what is S20_RESPOND)
//
// priority - priority to send packet at
//
// RETURNS: NONE
//
//
UINT S20FlushSendOrQueueControlPacket(
UINT what,
UINT correlator,
UINT who,
UINT priority)
{
UINT rc;
DebugEntry(S20FlushSendOrQueueControlPacket);
rc = S20FlushAndSendControlPacket(what, correlator, who, priority);
if (rc != 0)
{
// Let's queue this packet
if (((g_s20ControlPacketQTail + 1) % S20_MAX_QUEUED_CONTROL_PACKETS) ==
g_s20ControlPacketQHead)
{
//
// There is no more space in the control packet queue. We will
// discard everything from it and say the share ended because of
// a network error (if we're in a share).
//
ERROR_OUT(("No more space in control packet queue"));
}
else
{
S20CONTROLPACKETQENTRY *p = &(g_s20ControlPacketQ[g_s20ControlPacketQTail]);
p->who = who;
p->correlator = correlator;
p->what = what;
p->priority = priority;
g_s20ControlPacketQTail = (g_s20ControlPacketQTail + 1) %
S20_MAX_QUEUED_CONTROL_PACKETS;
rc = 0;
}
}
DebugExitDWORD(S20FlushSendOrQueueControlPacket, rc);
return rc;
}
//
// FUNCTION: S20FlushAndSendControlPacket
//
// DESCRIPTION:
//
// Attempts to flush any queued S20 control packets and then send a S20
// control packet. If sending fails then free the packet.
//
// PARAMETERS:
//
// what - which type of packet
//
// correlator - the correlator to place in the packet
//
// who - the target party (if what is a S20_DELETE) or the originator (if
// what is S20_RESPOND)
//
// priority - priority to send packet at
//
// RETURNS:
//
//
UINT S20FlushAndSendControlPacket
(
UINT what,
UINT correlator,
UINT who,
UINT priority
)
{
UINT rc;
PS20PACKETHEADER pS20Packet;
UINT length;
DebugEntry(S20FlushAndSendControlPacket);
//
// First try to flush.
//
rc = S20SendQueuedControlPackets();
if (rc != 0)
{
TRACE_OUT(("S20SendQueuedControlPackets %u", rc));
DC_QUIT;
}
rc = S20MakeControlPacket(what, correlator, who, &pS20Packet, &length, priority);
if (rc != 0)
{
TRACE_OUT(("S20MakeControlPacket %u", rc));
DC_QUIT;
}
TRACE_OUT(("CP %u %lu %u sent", what, correlator, who));
rc = S20SendControlPacket(pS20Packet, length, priority);
if (rc != 0)
{
TRACE_OUT(("S20SendControlPacket %u", rc));
DC_QUIT;
}
DC_EXIT_POINT:
DebugExitDWORD(S20FlushAndSendControlPacket, rc);
return(rc);
}
//
// FUNCTION: S20SendControlPacket
//
// DESCRIPTION:
//
// Attempts to send a S20 control packet. If sending fails then free the
// packet.
//
// PARAMETERS:
//
// pPacket - pointer to the control packet to send. These are always
// broadcast.
//
// length - length of aforementioned packet.
//
// priority - priority to send packet at
//
// RETURNS:
//
//
UINT S20SendControlPacket
(
PS20PACKETHEADER pS20Packet,
UINT length,
UINT priority
)
{
UINT rc;
DebugEntry(S20SendControlPacket);
TRACE_OUT(("S20SendControlPacket: sending packet type %x, size %d",
pS20Packet->packetType, length));
rc = MG_SendData( g_s20pmgClient,
(NET_PRIORITY)priority,
g_s20BroadcastID,
length,
(void **)&pS20Packet );
if (rc != 0)
{
TRACE_OUT(("MG_SendData %lx", rc));
}
if (pS20Packet != NULL)
{
//
// The packet was not freed by the NL - we will do it instead.
//
MG_FreeBuffer(g_s20pmgClient, (void **)&pS20Packet);
}
DebugExitDWORD(S20SendControlPacket, rc);
return(rc);
}
//
// FUNCTION: S20SendQueuedControlPackets
//
// DESCRIPTION:
//
// Sends as many queued packets as possible
//
// PARAMETERS:
//
//
// RETURNS:
//
// 0 - all queued packets have been sent.
//
//
UINT S20SendQueuedControlPackets(void)
{
PS20PACKETHEADER pS20Packet;
UINT length;
UINT rc;
UINT priority;
DebugEntry(S20SendQueuedControlPackets);
//
// Assume success until something fails.
//
rc = 0;
//
// While there are packets to send - try to send them
//
while (g_s20ControlPacketQTail != g_s20ControlPacketQHead)
{
S20CONTROLPACKETQENTRY *p = &(g_s20ControlPacketQ[g_s20ControlPacketQHead]);
priority = p->priority;
rc = S20MakeControlPacket(p->what, p->correlator, p->who,
&pS20Packet, &length, priority);
if (rc != 0)
{
//
// Failed to make the packet - give up.
//
WARNING_OUT(("S20MakeControlPacket failed error %u", rc));
break;
}
rc = S20SendControlPacket(pS20Packet, length, priority);
if (rc != 0)
{
//
// Failed to send the packet - give up.
//
break;
}
//
// Succesfully sent the queue packet - move the head of the queue
// along one.
//
g_s20ControlPacketQHead = (g_s20ControlPacketQHead + 1) %
S20_MAX_QUEUED_CONTROL_PACKETS;
}
DebugExitDWORD(S20SendQueuedControlPackets, rc);
return(rc);
}
//
// S20AttachConfirm()
//
// Handles the MCS attach confirmation
//
void S20AttachConfirm
(
NET_UID userId,
NET_RESULT result,
UINT callID
)
{
NET_CHANNEL_ID correlator;
UINT rc;
DebugEntry(S20AttachConfirm);
if (g_s20State == S20_ATTACH_PEND)
{
//
// Assume we need to clear up.
//
rc = NET_RC_S20_FAIL;
if (result == NET_RESULT_OK)
{
//
// We're in. Now try to join our channel and remember our
// userID.
//
g_s20LocalID = userId;
//
// We must join our single member channel for flow control
//
rc = MG_ChannelJoin(g_s20pmgClient,
&correlator,
g_s20LocalID);
if (rc == 0)
{
//
// Now join the broadcast channel
//
rc = MG_ChannelJoinByKey(g_s20pmgClient,
&correlator,
GCC_AS_CHANNEL_KEY);
if (rc != 0)
{
MG_ChannelLeave(g_s20pmgClient, g_s20LocalID);
}
}
if (rc == 0)
{
//
// It worked - make the state change.
//
g_s20State = S20_JOIN_PEND;
TRACE_OUT(("g_s20State is S20_JOIN_PEND"));
}
else
{
//
// Everything else is some sort of logic error (we will
// follow our recovery path).
//
WARNING_OUT(("ChannelJoin unexpected error %u", rc));
}
}
if (rc != 0)
{
//
// Something didn't work work out - clear up with a
// SHARE_ENDED if a create or join was pending.
//
if (result == NET_RESULT_OK)
{
//
// The attach succeeded so detach because the join
// failed and we want to go back to initialised state.
//
MG_Detach(g_s20pmgClient);
g_s20LocalID = 0;
}
//
// Now make the state change and generate event if
// necessary.
//
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
if (g_s20Pend)
{
g_s20Pend = 0;
SC_End();
}
}
}
DebugExitVOID(S20AttachConfirm);
}
//
// S20DetachIndication()
//
// Handles NET_EVENT_DETACH notification for a user
//
void S20DetachIndication
(
NET_UID userId,
UINT callID
)
{
DebugEntry(S20DetachIndication);
//
// There are three possibilities here
//
// 1. We have been forced out.
// 2. All remote users have detached.
// 3. A remote user has detached.
//
// 2 is effectively a 3 for each current remote user. We report 1 as a
// network error.
//
if (userId == g_s20LocalID)
{
//
// We have been forced out.
//
switch (g_s20State)
{
case S20_IN_SHARE:
case S20_SHARE_PEND:
case S20_SHARE_STARTING:
//
// Generate a share ended event.
//
SC_End();
// FALL THROUGH
case S20_NO_SHARE:
//
// Just revert to init state.
//
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
break;
case S20_JOIN_PEND:
case S20_ATTACH_PEND:
//
// Check the join or create pending flags here and if
// either one is set then generate a share ended
//
if (g_s20Pend)
{
g_s20Pend = 0;
SC_End();
}
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
break;
case S20_TERM:
case S20_INIT:
//
// Unusual but not impossible.
//
TRACE_OUT(("Ignored in state %u", g_s20State));
break;
default:
ERROR_OUT(("Invalid state %u", g_s20State));
break;
}
}
else
{
ASSERT(userId != NET_ALL_REMOTES);
//
// A single remote user has left.
//
switch (g_s20State)
{
case S20_IN_SHARE:
{
//
// If we are in a share then issue a PARTY_DELETED event
// for the appropriate party if they have been added.
// S20MaybeIssuePersonDelete will only issue deletes for
// parties which have been added succesfully.
//
S20MaybeIssuePersonDelete(userId);
}
break;
default:
{
//
// In any other state this makes no difference to us.
//
TRACE_OUT(("ignored in state %u", g_s20State));
}
break;
}
}
DebugExitVOID(S20DetachIndication);
}
//
// FUNCTION: S20JoinConfirm
//
// DESCRIPTION:
//
// Handles the NET_EVENT_CHANNEL_JOIN message from the NL
//
// PARAMETERS:
//
// pNetEventHeader - pointer to the event
//
// RETURNS: NONE
//
//
void S20JoinConfirm(PNET_JOIN_CNF_EVENT pJoinConfirm)
{
UINT rc;
DebugEntry(S20JoinConfirm);
if (g_s20State == S20_JOIN_PEND)
{
//
// Handle the join completing
//
if (pJoinConfirm->result == NET_RESULT_OK)
{
//
// We have sucessfully joined, either our single user
// channel or our broadcast channel
// We detect that both are successful when the g_s20BroadcastID
// field is filled in and g_s20JoinedLocal is TRUE
//
if (pJoinConfirm->channel == g_s20LocalID)
{
g_s20JoinedLocal = TRUE;
TRACE_OUT(("Joined user channel"));
}
else
{
//
// Store the assigned channel.
//
g_s20BroadcastID = pJoinConfirm->channel;
TRACE_OUT(("Joined channel %u", (UINT)g_s20BroadcastID));
}
//
// If we have joined both channels then let it rip
//
if (g_s20JoinedLocal && g_s20BroadcastID)
{
g_s20State = S20_NO_SHARE;
TRACE_OUT(("g_s20State is S20_NO_SHARE"));
DCS_NotifyUI(SH_EVT_APPSHARE_READY, TRUE, 0);
//
// Issue create or join if they are pending.
//
if (g_s20Pend != 0)
{
ASSERT(g_s20Pend == S20_JOIN);
UINT sPend;
sPend = g_s20Pend;
g_s20Pend = 0;
S20CreateOrJoinShare(sPend, pJoinConfirm->callID);
}
}
}
else
{
ERROR_OUT(("Channel join failed"));
//
// Clear up by reverting to initialised state.
//
MG_Detach(g_s20pmgClient);
g_s20LocalID = 0;
g_s20BroadcastID = 0;
g_s20JoinedLocal = FALSE;
//
// Now make the state change and generate event if
// necessary.
//
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
if (g_s20Pend)
{
g_s20Pend = 0;
SC_End();
}
}
}
DebugExitVOID(S20JoinConfirm);
}
//
// FUNCTION: S20LeaveIndication
//
// DESCRIPTION:
//
// Handles the NET_EV_LEAVE_INDICATION message from the NL
//
// PARAMETERS:
//
// pNetEventHeader - pointer to the event
//
// RETURNS: NONE
//
//
void S20LeaveIndication
(
NET_CHANNEL_ID channelID,
UINT callID
)
{
UINT rc;
DebugEntry(S20LeaveIndication);
//
// A leave indication means we were forced out of a channel. As we
// only use one channel this is bound to be terminal and we will
// generate appropriate share ending type events and detach (this will
// hopefully tell the remote systems we have gone - also we have no
// state which is attached but not trying to join so the alternatives
// would be to 1) add a new state or 2) try and re-join a channel
// immediately we get chucked out. Neither appeals.
//
switch (g_s20State)
{
case S20_IN_SHARE:
case S20_SHARE_PEND:
case S20_SHARE_STARTING:
//
// Generate a share ended event.
//
SC_End();
// FALL THROUGH
case S20_NO_SHARE:
case S20_JOIN_PEND:
case S20_ATTACH_PEND:
//
// Detach from the domain.
//
MG_Detach(g_s20pmgClient);
g_s20LocalID = 0;
//
// Check the join or create pending flags here and if either
// one is set then generate a share ended
//
if (g_s20Pend)
{
g_s20Pend = 0;
SC_End();
}
g_s20State = S20_INIT;
TRACE_OUT(("g_s20State is S20_INIT"));
break;
case S20_TERM:
case S20_INIT:
//
// Unusual but not impossible.
//
TRACE_OUT(("Ignored in state %u", g_s20State));
break;
default:
ERROR_OUT(("Invalid state %u", g_s20State));
break;
}
DebugExitVOID(S20LeaveIndication);
}
//
// S20SendIndication()
//
// Handles received data notification
//
void S20SendIndication(PNET_SEND_IND_EVENT pSendIndication)
{
PS20PACKETHEADER pS20Packet;
DebugEntry(S20SendIndication);
pS20Packet = (PS20PACKETHEADER)(pSendIndication->data_ptr);
//
// If App Sharing detaches from the T.120 conference, it will free up
// all data indication buffers. We need to check for this condition.
//
if (NULL != pS20Packet)
{
if (!(pS20Packet->packetType & S20_ALL_VERSIONS))
{
ERROR_OUT(("User is trying to connect from %#hx system",
pS20Packet->packetType & 0xF0));
//
// This should never happen, but if it does then we assert in the
// debug build and quietly fail in the retail build.
//
ERROR_OUT(("An unsupported version of app sharing joined the conference"));
DC_QUIT;
}
//
// Mask out the protocol version
//
switch (pS20Packet->packetType & S20_PACKET_TYPE_MASK)
{
case S20_CREATE:
S20CreateMsg((PS20CREATEPACKET)pS20Packet);
break;
case S20_JOIN:
S20JoinMsg((PS20JOINPACKET)pS20Packet);
break;
case S20_RESPOND:
S20RespondMsg((PS20RESPONDPACKET)pS20Packet);
break;
case S20_DELETE:
S20DeleteMsg((PS20DELETEPACKET)pS20Packet);
break;
case S20_LEAVE:
S20LeaveMsg((PS20LEAVEPACKET)pS20Packet);
break;
case S20_END:
S20EndMsg((PS20ENDPACKET)pS20Packet);
break;
case S20_COLLISION:
S20CollisionMsg((PS20COLLISIONPACKET)pS20Packet);
break;
case S20_DATA:
S20DataMsg((PS20DATAPACKET)pS20Packet);
break;
default:
ERROR_OUT(("invalid packet %hu", pS20Packet->packetType));
break;
}
}
DC_EXIT_POINT:
MG_FreeBuffer(g_s20pmgClient, (void **)&pSendIndication);
DebugExitVOID(S20SendIndication);
}
//
// FUNCTION: S20Flow
//
// DESCRIPTION:
//
// Handles the NET_FLOW event
//
// PARAMETERS:
//
// data1, data2 - the data from the UT event handler
//
// RETURNS: NONE
//
//
void S20Flow
(
UINT priority,
UINT newBufferSize
)
{
DebugEntry(S20Flow);
//
// We know this is our data channel (it is the only one we flow
// control) but if this is not the UPDATE stream, then ignore it.
// UPDATEs are low priority.
//
ASSERT(priority == NET_LOW_PRIORITY);
if (g_asSession.pShare != NULL)
{
TRACE_OUT(("Received flow control notification, new size %lu",
newBufferSize));
if (g_asSession.pShare->m_pHost != NULL)
{
//
// First try and improve the LAN performance by sending orders in
// large buffers, if we find that the throughput can handle it.
//
g_asSession.pShare->m_pHost->UP_FlowControl(newBufferSize);
//
// Adjust the depth which we try to spoil orders to based on
// feedback.
//
g_asSession.pShare->m_pHost->OA_FlowControl(newBufferSize);
}
//
// Tell DCS so that we can skip GDC compression.
// This improves responsiveness over high bandwidth links because it
// reduces the CPU loading on the sender
//
g_asSession.pShare->DCS_FlowControl(newBufferSize);
}
DebugExitVOID(S20Flow);
}
//
// FUNCTION: S20CreateMsg
//
// DESCRIPTION:
//
// Handles an incoming create message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the create message itself
//
// RETURNS: NONE
//
void S20CreateMsg
(
PS20CREATEPACKET pS20Packet
)
{
BOOL rc;
DebugEntry(S20CreateMsg);
TRACE_OUT(("S20_CREATE from [%d - %s], correlator %x",
pS20Packet->header.user, (LPSTR)pS20Packet->data,
pS20Packet->correlator));
//
// First of all check if the correlator on this CREATE is the same as
// our current view of the correlator. This may happen if a sweep
// RESPOND overtakes a CREATE - in this case we will create the share
// on the RESPOND and this is simply the delayed CREATE arriving now so
// we don't need to do anything here.
//
if (g_s20ShareCorrelator == pS20Packet->correlator)
{
WARNING_OUT(("Received S20_CREATE from [%d] with bogus correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
DC_QUIT;
}
if ((g_s20State == S20_NO_SHARE) ||
((g_s20State == S20_SHARE_PEND) &&
(g_s20ShareCorrelator == 0)))
{
//
// Either there is no share or we have issued a join. In these
// curcumstances we want to try to accept the create message.
//
//
// Remember the share correlator.
//
g_s20ShareCorrelator = pS20Packet->correlator;
//
// Start the share
// CHECK FOR FAILURE FOR THE FIRST ONE.
//
rc = SC_Start(g_s20LocalID);
if (rc)
{
g_s20State = S20_SHARE_STARTING;
TRACE_OUT(("g_s20State is S20_SHARE_STARTING"));
rc = S20MaybeAddNewParty(pS20Packet->header.user,
pS20Packet->lenCaps, pS20Packet->lenName,
pS20Packet->data);
}
if (!rc)
{
//
// Something went wrong. Kill the share, this will clean up
// everything.
//
SC_End();
}
}
else if ((g_s20State == S20_SHARE_PEND) ||
(g_s20State == S20_SHARE_STARTING) ||
(g_s20State == S20_IN_SHARE))
{
//
// Only current share creator should tell other dude there's an
// error.
//
if (S20_GET_CREATOR(g_s20ShareCorrelator) == g_s20LocalID)
{
//
// If we know about a share already then ignore this one.
//
WARNING_OUT(("Received S20_CREATE from [%d] with correlator %x, share colllision",
pS20Packet->header.user, pS20Packet->correlator));
S20FlushSendOrQueueControlPacket(S20_END,
pS20Packet->correlator, 0, NET_TOP_PRIORITY);
S20FlushSendOrQueueControlPacket(S20_COLLISION,
pS20Packet->correlator, 0, NET_TOP_PRIORITY);
}
}
DC_EXIT_POINT:
DebugExitVOID(S20CreateMsg);
}
//
// FUNCTION: S20JoinMsg
//
// DESCRIPTION:
//
// Handles an incoming join message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the join message itself
//
// RETURNS: NONE
//
void S20JoinMsg
(
PS20JOINPACKET pS20Packet
)
{
DebugEntry(S20JoinMsg);
TRACE_OUT(("S20_JOIN from [%d - %s]",
pS20Packet->header.user, (LPSTR)pS20Packet->data));
switch (g_s20State)
{
case S20_SHARE_PEND:
//
// If we receive a join when a share is pending which we are
// creating then we will try to add the party. If it succeeds
// then we will respond to the join as we would if we were in a
// share (and we will indeed then be in a share). If it fails
// we will delete the joiner.
//
// If we receive a join when a share is pending because we are
// trying to join (ie simultaneous joiners) then we can just
// ignore it because a party which is joining a share will send
// a respond as soon as they know the correlator for the share
// they have succesfully joined. This respond will be ignored
// by any parties which saw and added the new party but it will
// be seen by any simultaneous joiners and they will then get a
// chance to try and add the other joiner. If this fails they
// will then do the normal processing for a failure handling a
// respond message when we joined a share (ie delete
// themselves).
//
// This will potentially mean that simultaneous joiners will
// cause each other to delete themselves when there was room
// for one of them in the share - we accept this.
//
//
// Why is the share pending? If the correlator is non-zero
// then we are creating a share.
//
if (g_s20ShareCorrelator != 0)
{
//
// We are creating a share - treat this like a respond.
//
if (!SC_Start(g_s20LocalID))
{
WARNING_OUT(("S20Join: couldn't create share, clean up"));
SC_End();
}
else
{
g_s20State = S20_SHARE_STARTING;
TRACE_OUT(("g_s20State is S20_SHARE_STARTING"));
S20MaybeAddNewParty(pS20Packet->header.user,
pS20Packet->lenCaps, pS20Packet->lenName,
pS20Packet->data);
}
}
else
{
//
// We are joining a share - simultaneous joiners.
//
TRACE_OUT(("Simultaneous joiner - ignored for now, expect a respond"));
}
break;
case S20_IN_SHARE:
case S20_SHARE_STARTING:
{
//
// When we are in a share we will try and add this person then
// give them a respond or a delete depending on what we did.
//
S20MaybeAddNewParty(pS20Packet->header.user,
pS20Packet->lenCaps, pS20Packet->lenName,
pS20Packet->data);
break;
}
default:
break;
}
DebugExitVOID(S20JoinMsg);
}
//
// FUNCTION: S20RespondMsg
//
// DESCRIPTION:
//
// Handles an incoming respond message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the respond message itself
//
// RETURNS: NONE
//
void S20RespondMsg
(
PS20RESPONDPACKET pS20Packet
)
{
BOOL rc;
DebugEntry(S20RespondMsg);
TRACE_OUT(("S20_RESPOND from [%d - %s], for [%d], correlator %x",
pS20Packet->header.user, pS20Packet->data, pS20Packet->originator,
pS20Packet->correlator));
//
// First filter the incoming respond messages as follows.
//
// If we know what share we are in and this does not have the same
// correlator then respond with a delete and don't process any further.
//
// If the respond is not a response for us (ie we are not the
// originator and it is not a sweep-up response (the originator equals
// zero) then ignore it.
//
if ((g_s20ShareCorrelator != 0) &&
(pS20Packet->correlator != g_s20ShareCorrelator))
{
//
// Make sure sender knows we're not in this share.
//
TRACE_OUT(("S20_RESPOND from [%d] with unknown correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
S20FlushSendOrQueueControlPacket(S20_LEAVE,
pS20Packet->correlator, 0, NET_TOP_PRIORITY);
DC_QUIT;
}
//
// Now handle incoming message according to state.
//
switch (g_s20State)
{
case S20_SHARE_PEND:
if ((pS20Packet->originator == g_s20LocalID) ||
(pS20Packet->originator == 0))
{
//
// A respond in share pending and it is for us. First,
// start a share.
//
rc = SC_Start(g_s20LocalID);
if (!rc)
{
SC_End();
}
else
{
g_s20State = S20_SHARE_STARTING;
TRACE_OUT(("g_s20State is S20_SHARE_STARTING"));
//
// Why is the share pending? If the correlator is non-zero
// then we are creating a share.
//
if (g_s20ShareCorrelator == 0)
{
//
// We are joining a share so do nothing if we fail (we
// will move back to NO_SHARE state if this happens).
//
g_s20ShareCorrelator = pS20Packet->correlator;
}
//
// Now try and add this new party.
//
rc = S20MaybeAddNewParty(pS20Packet->header.user,
pS20Packet->lenCaps, pS20Packet->lenName,
pS20Packet->data);
if (!rc)
{
//
// The responding party has been rejected by us. What
// happens next depends on whether we are creating the
// share or not.
//
if (S20_GET_CREATOR(g_s20ShareCorrelator) != g_s20LocalID)
{
//
// We are not creating (ie we are joining) and we
// have failed to add a party so end the share
// (indicating that we are rejecting the remote
// party).
//
SC_End();
}
//
// If we were creating the share then there is nothing
// to do - just stay in SHARE_STARTING waiting for the
// next response.
//
}
}
}
break;
case S20_IN_SHARE:
case S20_SHARE_STARTING:
//
// Who created this share. If it was us then we want to
// delete people we reject, otherwise we want to leave if we
// reject people.
//
//
// Now try and add this new party. Of course it is entirely
// possible that we've already added them at this stage - but
// S20MaybeAddNewParty will just pretend to add them and return
// if that's the case.
//
rc = S20MaybeAddNewParty(pS20Packet->header.user,
pS20Packet->lenCaps, pS20Packet->lenName,
pS20Packet->data);
if (!rc)
{
WARNING_OUT(("Couldn't add [%d] to our share party list",
pS20Packet->header.user));
}
break;
default:
break;
}
DC_EXIT_POINT:
DebugExitVOID(S20RespondMsg);
}
//
// FUNCTION: S20DeleteMsg
//
// DESCRIPTION:
//
// Handles an incoming delete message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the delete message itself
//
// RETURNS: NONE
//
void S20DeleteMsg
(
PS20DELETEPACKET pS20Packet
)
{
DebugEntry(S20DeleteMsg);
TRACE_OUT(("S20_DELETE from [%d], for [%d], correlator %x",
pS20Packet->header.user, pS20Packet->target, pS20Packet->correlator));
//
// ONLY SHARE CREATOR can delete people from share
//
if (!g_s20ShareCorrelator)
{
WARNING_OUT(("S20_DELETE, ignoring we're not in a share"));
DC_QUIT;
}
if (pS20Packet->target != g_s20LocalID)
{
//
// Not for us, ignore.
//
DC_QUIT;
}
if (g_s20ShareCorrelator != pS20Packet->correlator)
{
WARNING_OUT(("Received S20_DELETE from [%d] with unknown correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
S20FlushSendOrQueueControlPacket(S20_LEAVE, pS20Packet->correlator,
0, NET_TOP_PRIORITY);
DC_QUIT;
}
if (S20_GET_CREATOR(g_s20ShareCorrelator) != pS20Packet->header.user)
{
WARNING_OUT(("Received S20_DELETE from [%d] who did not create share, ignore",
pS20Packet->header.user));
DC_QUIT;
}
//
// Now handle incoming messages according to state.
//
switch (g_s20State)
{
case S20_SHARE_PEND:
case S20_SHARE_STARTING:
//
// Just tell everyone else we're leaving and then issue a
// SHARE_ENDED event.
//
TRACE_OUT(("CP LEAVE %lu %d", g_s20ShareCorrelator, 0));
S20FlushSendOrQueueControlPacket(S20_LEAVE,
g_s20ShareCorrelator,
0,
NET_TOP_PRIORITY);
// FALL THROUGH
case S20_IN_SHARE:
SC_End();
g_s20State = S20_NO_SHARE;
TRACE_OUT(("g_s20State is S20_NO_SHARE"));
break;
default:
break;
}
DC_EXIT_POINT:
DebugExitVOID(S20DeleteMsg);
}
//
// FUNCTION: S20LeaveMsg
//
// DESCRIPTION:
//
// Handles an incoming leave message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the leave message itself
//
// RETURNS: NONE
//
void S20LeaveMsg(PS20LEAVEPACKET pS20Packet)
{
DebugEntry(S20LeaveMsg);
TRACE_OUT(("S20_LEAVE from [%d], correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
if (!g_s20ShareCorrelator)
{
WARNING_OUT(("S20_LEAVE, ignoring we're not in a share"));
DC_QUIT;
}
if (g_s20ShareCorrelator != pS20Packet->correlator)
{
WARNING_OUT(("Received S20_LEAVE from [%d] for unknown correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
DC_QUIT;
}
switch (g_s20State)
{
case S20_IN_SHARE:
//
// We only need to handle this when we are in a share.
//
S20MaybeIssuePersonDelete(pS20Packet->header.user);
break;
default:
break;
}
DC_EXIT_POINT:
DebugExitVOID(S20LeaveMsg);
}
//
// FUNCTION: S20EndMsg
//
// DESCRIPTION:
//
// Handles an incoming end message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the end message itself
//
// RETURNS: NONE
//
void S20EndMsg(PS20ENDPACKET pS20Packet)
{
DebugEntry(S20EndMsg);
TRACE_OUT(("S20_END from [%d], correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
if (!g_s20ShareCorrelator)
{
// We don't care
WARNING_OUT(("S20_END ignored, not in share"));
DC_QUIT;
}
if (g_s20ShareCorrelator != pS20Packet->correlator)
{
//
// Just discard this.
//
WARNING_OUT(("Received S20_END from [%d] with unknown correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
DC_QUIT;
}
//
// Only the share creator can end the share.
//
if (S20_GET_CREATOR(g_s20ShareCorrelator) != pS20Packet->header.user)
{
WARNING_OUT(("Received S20_END from [%d] who did not create share, simply remove from user list.",
pS20Packet->header.user));
if (g_s20State == S20_IN_SHARE)
{
S20MaybeIssuePersonDelete(pS20Packet->header.user);
}
DC_QUIT;
}
switch (g_s20State)
{
case S20_IN_SHARE:
case S20_SHARE_PEND:
case S20_SHARE_STARTING:
//
// We just need to generate a share ended event.
//
SC_End();
g_s20State = S20_NO_SHARE;
TRACE_OUT(("g_s20State is S20_NO_SHARE"));
break;
default:
break;
}
DC_EXIT_POINT:
DebugExitVOID(S20EndMsg);
}
//
// S20CollisionMsg()
//
// DESCRIPTION:
//
// Handles an incoming collision message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the collision message itself
//
// RETURNS: NONE
//
void S20CollisionMsg(PS20COLLISIONPACKET pS20Packet)
{
DebugEntry(S20CollisionMsg);
TRACE_OUT(("S20_COLLISION from [%d], correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
if (!g_s20ShareCorrelator)
{
// We don't care
WARNING_OUT(("S20_COLLISION ignored, not in share"));
DC_QUIT;
}
if (g_s20ShareCorrelator != pS20Packet->correlator)
{
//
// Just discard this.
//
WARNING_OUT(("Received S20_COLLISION from [%d] with unknown correlator %x",
pS20Packet->header.user, pS20Packet->correlator));
DC_QUIT;
}
//
// If we created our own share, but got a collision from the remote,
// then kill our share.
//
if (S20_GET_CREATOR(g_s20ShareCorrelator) != g_s20LocalID)
{
TRACE_OUT(("S20_COLLISION ignored, we didn't create share"));
DC_QUIT;
}
switch (g_s20State)
{
case S20_IN_SHARE:
case S20_SHARE_PEND:
case S20_SHARE_STARTING:
//
// We just need to generate a share ended event.
//
SC_End();
g_s20State = S20_NO_SHARE;
TRACE_OUT(("g_s20State is S20_NO_SHARE"));
break;
default:
break;
}
DC_EXIT_POINT:
DebugExitVOID(S20CollisionMsg);
}
//
// FUNCTION: S20DataMsg
//
// DESCRIPTION:
//
// Handles an incoming data message.
//
// PARAMETERS:
//
// pS20Packet - pointer to the data message itself
//
// RETURNS: TRUE - free the event, FALSE - do not free the event
//
void S20DataMsg(PS20DATAPACKET pS20Packet)
{
DebugEntry(S20DataMsg);
ASSERT(!IsBadWritePtr(pS20Packet, sizeof(S20DATAPACKET)));
ASSERT(!IsBadWritePtr(pS20Packet, sizeof(S20DATAPACKET) - sizeof(DATAPACKETHEADER) +
pS20Packet->dataLength));
//
// Check if we're interseted in this data.
//
if ((pS20Packet->correlator == g_s20ShareCorrelator) &&
(g_s20State == S20_IN_SHARE) &&
g_asSession.pShare)
{
//
// Return it.
//
g_asSession.pShare->SC_ReceivedPacket(pS20Packet);
}
DebugExitVOID(S20DataMsg);
}
//
// FUNCTION: S20MaybeAddNewParty
//
// DESCRIPTION:
//
// If the specified party has not already been added then try to add them
// now.
//
// PARAMETERS:
//
// userID - the new party's network user ID.
// lenCaps - the length of the new party's capabilities.
// lenName - the length of the new party's name.
// pData - a pointer to the new party's data which contains the name
// followed by the capabilities data.
//
// RETURNS:
// BOOL for success
//
BOOL S20MaybeAddNewParty
(
MCSID mcsID,
UINT lenCaps,
UINT lenName,
LPBYTE pData
)
{
BOOL rc = FALSE;
UINT oldState;
LPBYTE pCaps = NULL;
BOOL memAllocated = FALSE;
DebugEntry(S20MaybeAddNewParty);
//
// If we don't have a share, fail.
//
if (!g_asSession.pShare)
{
WARNING_OUT(("No ASShare; ignoring add party for [%d]", mcsID));
DC_QUIT;
}
//
// Check if this party has already been added.
//
if (g_asSession.pShare->SC_ValidateNetID(mcsID, NULL))
{
TRACE_OUT(("S20MaybeAddNewParty: already added %u", mcsID));
rc = TRUE;
DC_QUIT;
}
//
// We need the caps structure to be 4-byte aligned. It currently
// follows a variable-length name string and may therefore not be
// aligned. If it is not aligned, we allocate an aligned buffer and
// copy it there.
//
if (0 != (((UINT_PTR)pData + lenName) % 4))
{
TRACE_OUT(("Capabilities data is unaligned - realigning"));
//
// Get a 4-byte aligned buffer for the capabilities data.
//
pCaps = new BYTE[lenCaps];
if (!pCaps)
{
ERROR_OUT(("Could not allocate %u bytes for aligned caps.",
lenCaps));
DC_QUIT;
}
//
// Flag so we know to free the memory later.
//
memAllocated = TRUE;
//
// Copy the caps data into our 4-byte aligned memory block.
//
memcpy(pCaps, (pData + lenName), lenCaps);
}
else
{
//
// The capabilities data is already aligned so we don't need to
// move it.
//
pCaps = pData + lenName;
}
//
// Make sure we are in a share before we issue person add events.
//
oldState = g_s20State;
g_s20State = S20_IN_SHARE;
TRACE_OUT(("g_s20State is S20_IN_SHARE"));
//
// Attempt to add the new party.
//
rc = g_asSession.pShare->SC_PartyAdded(mcsID, (LPSTR)pData, lenCaps, pCaps);
if (rc)
{
//
// The new party has been accepted so send a response packet. Do
// this at ALL priorities, so it gets there before any type of data
// at one particular priority.
//
TRACE_OUT(("CP RESPOND %lu %d", g_s20ShareCorrelator, 0));
S20FlushSendOrQueueControlPacket(S20_RESPOND, g_s20ShareCorrelator,
mcsID, NET_TOP_PRIORITY | NET_SEND_ALL_PRIORITIES);
}
else
{
g_asSession.pShare->SC_PartyDeleted(mcsID);
//
// Reset the state back to what it was if we failed.
//
g_s20State = oldState;
TRACE_OUT(("g_s20State is %u", g_s20State));
if (S20_GET_CREATOR(g_s20ShareCorrelator) == g_s20LocalID)
{
//
// The new party has been rejected so send a delete packet.
//
TRACE_OUT(("CP DELETE %lu %u", g_s20ShareCorrelator, mcsID));
S20FlushSendOrQueueControlPacket(S20_DELETE, g_s20ShareCorrelator,
mcsID, NET_TOP_PRIORITY);
}
}
DC_EXIT_POINT:
//
// Free memory used to store aligned caps.
//
if (memAllocated)
{
delete[] pCaps;
}
DebugExitBOOL(S20MaybeAddNewParty, rc);
return(rc);
}
//
// FUNCTION: S20NewCorrelator
//
// DESCRIPTION:
//
// Returns a new correlator for us to use when we are creating a share.
// This is a combination of our mcsID (low 16 bits in Intel format) and
// a generation count (high 16 bits in Intel format).
//
// PARAMETERS: NONE
//
// RETURNS: the new correlator
//
//
UINT S20NewCorrelator(void)
{
UINT correlator;
DebugEntry(S20NewCorrelator);
g_s20Generation++;
correlator = g_s20LocalID | (((UINT)(g_s20Generation & 0xFFFF)) << 16);
DebugExitDWORD(S20NewCorrelator, correlator);
return(correlator);
}
//
// FUNCTION: S20MaybeIssuePersonDelete
//
// DESCRIPTION:
//
// If the supplied person is in the share then issue a PARTY_DELETED event
// for them.
//
// PARAMTERS:
//
// mcsID - a network personID
//
// reason - the reason code to use
//
// RETURNS: NONE
//
//
void S20MaybeIssuePersonDelete(MCSID mcsID)
{
DebugEntry(S20MaybeIssuePersonDelete);
if (g_asSession.pShare)
{
g_asSession.pShare->SC_PartyDeleted(mcsID);
}
//
// HET will kill the share if there aren't any hosts left. So we
// don't need to do anything here.
//
DebugExitVOID(S20MaybeIssuePersonDelete);
}
//
// FUNCTION: S20StreamToS20Priority
//
// DESCRIPTION:
//
// Converts a stream ID into a NET priority
//
// PARAMETERS:
//
// streamID - the stream ID.
//
// RETURNS: the priority
//
//
const NET_PRIORITY c_StreamS20Priority[NUM_PROT_STR - 1] =
{
NET_LOW_PRIORITY, // PROT_STR_UPDATES
NET_MEDIUM_PRIORITY, // PROT_STR_MISC
NET_MEDIUM_PRIORITY, // PROT_STR_UNUSED
NET_MEDIUM_PRIORITY, // PROT_STR_INPUT
};
NET_PRIORITY S20StreamToS20Priority(UINT streamID)
{
NET_PRIORITY priority;
DebugEntry(S20StreamToS20Priority);
ASSERT(streamID > PROT_STR_INVALID);
ASSERT(streamID < NUM_PROT_STR);
ASSERT(streamID != PROT_STR_UNUSED);
priority = c_StreamS20Priority[streamID - 1];
DebugExitDWORD(S20StreamToS20Priority, priority);
return(priority);
}