|
|
#include "precomp.h"
//
// SC.CPP
// Share Controller
//
// NOTE:
// We must take the UTLOCK_AS every time we
// * create/destroy the share object
// * add/remove a person from the share
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_CORE
//
// SC_Init()
// Initializes the share controller
//
BOOL SC_Init(void) { BOOL rc = FALSE;
DebugEntry(SC_Init);
ASSERT(!g_asSession.callID); ASSERT(!g_asSession.gccID); ASSERT(g_asSession.scState == SCS_TERM);
//
// Register as a Call Manager Secondary task
//
if (!CMS_Register(g_putAS, CMTASK_DCS, &g_pcmClientSc)) { ERROR_OUT(( "Failed to register with CMS")); DC_QUIT; }
g_asSession.scState = SCS_INIT; TRACE_OUT(("g_asSession.scState is SCS_INIT"));
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(SC_Init, rc); return(rc); }
//
// SC_Term()
//
// See sc.h for description.
//
//
void SC_Term(void) { DebugEntry(SC_Term);
//
// Clear up the core's state by generating appropriate PARTY_DELETED and
// SHARE_ENDED events.
//
switch (g_asSession.scState) { case SCS_SHARING: case SCS_SHAREENDING: case SCS_SHAREPENDING: SC_End(); break; }
//
// Deregister from the Call Manager
//
if (g_pcmClientSc) { CMS_Deregister(&g_pcmClientSc); }
g_asSession.gccID = 0; g_asSession.callID = 0; g_asSession.attendeePermissions = NM_PERMIT_ALL;
g_asSession.scState = SCS_TERM; TRACE_OUT(("g_asSession.scState is SCS_TERM"));
DebugExitVOID(SC_Term); }
//
// SC_CreateShare()
// Creates a new share or joins an existing one
//
BOOL SC_CreateShare(UINT s20CreateOrJoin) { BOOL rc = FALSE;
DebugEntry(SC_CreateShare);
//
// If we are initialised but there is no Call Manager call, return the
// race condition.
//
if ((g_asSession.scState != SCS_INIT) && (g_asSession.scState != SCS_SHAREPENDING)) { TRACE_OUT(("Ignoring SC_CreateShare() request; in bad state %d", g_asSession.scState)); DC_QUIT; }
if (!g_asSession.callID) { WARNING_OUT(("Ignoring SC_CreateShare() request; not in T120 call")); DC_QUIT; }
//
// Remember that we created this share.
//
g_asSession.fShareCreator = (s20CreateOrJoin == S20_CREATE); TRACE_OUT(("CreatedShare is %s", (s20CreateOrJoin == S20_CREATE) ? "TRUE" : "FALSE"));
g_asSession.scState = SCS_SHAREPENDING; TRACE_OUT(("g_asSession.scState is SCS_SHAREPENDING"));
rc = S20CreateOrJoinShare(s20CreateOrJoin, g_asSession.callID); if (!rc) { WARNING_OUT(("%s failed", (s20CreateOrJoin == S20_CREATE ? "S20_CREATE" : "S20_JOIN"))); }
DC_EXIT_POINT: DebugExitBOOL(SC_CreateShare, rc); return(rc); }
//
// SC_EndShare()
// This will end a share if we are in one or in the middle of establishing
// one, and clean up afterwards.
//
void SC_EndShare(void) { DebugEntry(SC_EndShare);
if (g_asSession.scState <= SCS_SHAREENDING) { TRACE_OUT(("Ignoring SC_EndShare(); nothing to do in state %d", g_asSession.scState)); } else { //
// If we are in a share or in the middle of creating/joining one, stop
// the process.
//
//
// Kill the share
// NOTE that this will call SC_End(), when we come back
// from this function our g_asSession.scState() should be SCS_INIT.
//
g_asSession.scState = SCS_SHAREENDING; TRACE_OUT(("g_asSession.scState is SCS_SHAREENDING"));
S20LeaveOrEndShare();
g_asSession.scState = SCS_INIT; }
DebugExitVOID(SC_EndShare); }
//
// SC_PersonFromNetID()
//
ASPerson * ASShare::SC_PersonFromNetID(UINT_PTR mcsID) { ASPerson * pasPerson;
DebugEntry(SC_PersonFromNetID);
ASSERT(mcsID != MCSID_NULL);
//
// Search for the mcsID.
//
if (!SC_ValidateNetID(mcsID, &pasPerson)) { ERROR_OUT(("Invalid [%u]", mcsID)); }
DebugExitPVOID(ASShare::SC_PersonFromNetID, pasPerson); return(pasPerson); }
//
// SC_ValidateNetID()
//
BOOL ASShare::SC_ValidateNetID ( UINT_PTR mcsID, ASPerson * * ppasPerson ) { BOOL rc = FALSE; ASPerson * pasPerson;
DebugEntry(ASShare::SC_ValidateNetID);
// Init to empty
pasPerson = NULL;
//
// MCSID_NULL matches no one.
//
if (mcsID == MCSID_NULL) { WARNING_OUT(("SC_ValidateNetID called with MCSID_NULL")); DC_QUIT; }
//
// Search for the mcsID.
//
for (pasPerson = m_pasLocal; pasPerson != NULL; pasPerson = pasPerson->pasNext) { ValidatePerson(pasPerson);
if (pasPerson->mcsID == mcsID) { //
// Found required person, set return values and quit
//
rc = TRUE; break; } }
DC_EXIT_POINT: if (ppasPerson) { *ppasPerson = pasPerson; }
DebugExitBOOL(ASShare::SC_ValidateNetID, rc); return(rc); }
//
// SC_PersonFromGccID()
//
ASPerson * ASShare::SC_PersonFromGccID(UINT gccID) { ASPerson * pasPerson;
// DebugEntry(ASShare::SC_PersonFromGccID);
for (pasPerson = m_pasLocal; pasPerson != NULL; pasPerson = pasPerson->pasNext) { ValidatePerson(pasPerson);
if (pasPerson->cpcCaps.share.gccID == gccID) { // Found it
break; } }
// DebugExitPVOID(ASShare::SC_PersonFromGccID, pasPerson);
return(pasPerson); }
//
// SC_Start()
// Inits the share (if all is OK), and adds local person to it.
//
BOOL SC_Start(UINT mcsID) { BOOL rc = FALSE; ASShare * pShare; ASPerson * pasPerson;
DebugEntry(SC_Start);
ASSERT(g_asSession.callID); ASSERT(g_asSession.gccID);
if ((g_asSession.scState != SCS_INIT) && (g_asSession.scState != SCS_SHAREPENDING)) { WARNING_OUT(("Ignoring SC_Start(); in bad state")); DC_QUIT; } if (g_asSession.pShare) { WARNING_OUT(("Ignoring SC_Start(); have ASShare object already")); DC_QUIT; }
g_asSession.scState = SCS_SHARING; TRACE_OUT(("g_asSession.scState is SCS_SHARING"));
#ifdef _DEBUG
//
// Use this to calculate time between joining share and getting
// first view
//
g_asSession.scShareTime = ::GetTickCount(); #endif // _DEBUG
//
// Allocate the share object and add the local dude to the share.
//
pShare = new ASShare; if (pShare) { ZeroMemory(pShare, sizeof(*(pShare))); SET_STAMP(pShare, SHARE);
rc = pShare->SC_ShareStarting(); }
UT_Lock(UTLOCK_AS); g_asSession.pShare = pShare; UT_Unlock(UTLOCK_AS);
if (!rc) { ERROR_OUT(("Can't create/init ASShare")); DC_QUIT; }
DCS_NotifyUI(SH_EVT_SHARE_STARTED, 0, 0);
//
// Join local dude into share. If that fails, also bail out.
//
pasPerson = g_asSession.pShare->SC_PartyJoiningShare(mcsID, g_asSession.achLocalName, sizeof(g_cpcLocalCaps), &g_cpcLocalCaps); if (!pasPerson) { ERROR_OUT(("Local person not joined into share successfully")); DC_QUIT; }
//
// Okay! We have a share, and it's set up.
//
//
// Tell the UI that we're in the share.
//
DCS_NotifyUI(SH_EVT_PERSON_JOINED, pasPerson->cpcCaps.share.gccID, 0);
//
// Start periodic processing if ViewSelf or record/playback is on.
//
if (g_asSession.pShare->m_scfViewSelf) { SCH_ContinueScheduling(SCH_MODE_NORMAL); }
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(SC_Start, rc); return(rc); }
//
// SC_End()
// Removes any remotes from the share, removes the local person, and
// cleans up after the fact.
//
void SC_End(void) { DebugEntry(SC_End);
if (g_asSession.scState < SCS_SHAREENDING) { TRACE_OUT(("Ignoring SC_EVENT_SHAREENDED")); } else { if (g_asSession.pShare) { g_asSession.pShare->SC_ShareEnded();
UT_Lock(UTLOCK_AS);
delete g_asSession.pShare; g_asSession.pShare = NULL;
UT_Unlock(UTLOCK_AS);
DCS_NotifyUI(SH_EVT_SHARE_ENDED, 0, 0); }
//
// If the previous state was SCS_SHARE_PENDING then we
// may have ended up here as the result of a 'back off' after
// trying to create two shares. Let's try to join again...
//
if (g_asSession.fShareCreator) { g_asSession.fShareCreator = FALSE; TRACE_OUT(("CreatedShare is FALSE"));
if (g_asSession.scState == SCS_SHAREPENDING) { WARNING_OUT(("Got share end while share pending - retry join")); UT_PostEvent(g_putAS, g_putAS, 0, CMS_NEW_CALL, 0, 0); } }
g_asSession.scState = SCS_INIT; TRACE_OUT(("g_asSession.scState is SCS_INIT")); }
g_s20ShareCorrelator = 0;
//
// Reset the queued control packets.
//
g_s20ControlPacketQHead = 0; g_s20ControlPacketQTail = 0;
g_s20State = S20_NO_SHARE; TRACE_OUT(("g_s20State is S20_NO_SHARE"));
DebugExitVOID(SC_End); }
//
// SC_PartyAdded()
//
BOOL ASShare::SC_PartyAdded ( UINT mcsID, LPSTR szName, UINT cbCaps, LPVOID pCaps ) { BOOL rc = FALSE; ASPerson * pasPerson;
if (g_asSession.scState != SCS_SHARING) { WARNING_OUT(("Ignoring SC_EVENT_PARTYADDED; not in share")); DC_QUIT; }
ASSERT(g_asSession.callID); ASSERT(g_asSession.gccID);
//
// A remote party is joining the share
//
//
// Notify everybody
//
pasPerson = SC_PartyJoiningShare(mcsID, szName, cbCaps, pCaps); if (!pasPerson) { WARNING_OUT(("SC_PartyJoiningShare failed for remote [%d]", mcsID)); DC_QUIT; }
//
// SYNC now
// We should NEVER SEND ANY PACKETS when syncing. We aren't ready
// because we haven't joined the person into the share. yet.
// So we simply set variables for us to send data off the next
// periodic schedule.
//
#ifdef _DEBUG
m_scfInSync = TRUE; #endif // _DEBUG
//
// Stuff needed for being in the share, hosting or not
//
DCS_SyncOutgoing(); OE_SyncOutgoing();
if (m_pHost != NULL) { //
// Common to both starting sharing and retransmitting sharing info
//
m_pHost->HET_SyncCommon(); m_pHost->HET_SyncAlreadyHosting(); m_pHost->CA_SyncAlreadyHosting(); }
#ifdef _DEBUG
m_scfInSync = FALSE; #endif // _DEBUG
//
// DO THIS LAST -- tell the UI this person is in the share.
//
DCS_NotifyUI(SH_EVT_PERSON_JOINED, pasPerson->cpcCaps.share.gccID, 0);
//
// Start periodic processing
//
SCH_ContinueScheduling(SCH_MODE_NORMAL);
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(ASShare::SC_PartyAdded, rc); return(rc); }
//
// SC_PartyDeleted()
//
void ASShare::SC_PartyDeleted(UINT_PTR mcsID) { if ((g_asSession.scState != SCS_SHARING) && (g_asSession.scState != SCS_SHAREENDING)) { WARNING_OUT(("Ignoring SC_EVENT_PARTYDELETED; wrong state")); DC_QUIT; }
SC_PartyLeftShare(mcsID);
DC_EXIT_POINT: DebugExitVOID(ASShare::SC_PartyDeleted); }
//
// SC_ReceivedPacket()
//
void ASShare::SC_ReceivedPacket(PS20DATAPACKET pPacket) { ASPerson * pasPerson; PSNIPACKET pSNIPacket;
DebugEntry(ASShare::SC_ReceivedPacket);
if (g_asSession.scState != SCS_SHARING) { WARNING_OUT(("Ignoring received data because we're not in right state")); DC_QUIT; }
//
// Ignore packets on streams we don't know about.
//
if ((pPacket->stream < SC_STREAM_LOW) || (pPacket->stream > SC_STREAM_HIGH)) { TRACE_OUT(("Ignoring received data on unrecognized stream %d", pPacket->stream)); DC_QUIT; }
//
// It is possible to get a packet from a person we do not know
// about.
//
// This can happen if we join a conference that has an existing
// share session. All existing parties will be sending data on
// the channels we have joined but we will not yet have
// received the events to add them to our share session.
//
// Data packets from unknown people are ignored (ignoring this
// data is OK as we will resync with them when they are added
// to our share session)
//
if (!SC_ValidateNetID(pPacket->header.user, &pasPerson)) { WARNING_OUT(("Ignoring data packet from unknown person [%d]", pPacket->header.user)); DC_QUIT; }
if (pPacket->data.dataType == DT_SNI) { //
// This is an SNI packet - handle it here.
//
pSNIPacket = (PSNIPACKET)pPacket;
switch(pSNIPacket->message) { case SNI_MSG_SYNC: //
// This is a sync message.
//
if (pSNIPacket->destination == m_pasLocal->mcsID) { //
// This sync message is for us.
//
pasPerson->scSyncRecStatus[pPacket->stream-1] = SC_SYNCED; } else { TRACE_OUT(("Ignoring SYNC on stream %d for [%d] from [%d]", pPacket->stream, pSNIPacket->destination, pPacket->header.user)); } break;
default: ERROR_OUT(("Unknown SNI message %u", pSNIPacket->message)); break; } } else if (pasPerson->scSyncRecStatus[pPacket->stream-1] == SC_SYNCED) { PS20DATAPACKET pPacketUse; UINT cbBufferSize; UINT compression; BOOL decompressed; UINT dictionary;
//
// Decompress the packet if necessary
//
//
// Use the temporary buffer. This will never fail, so we don't
// need to check the return value. THIS MEANS THAT THE HANDLING OF
// INCOMING PACKETS CAN NEVER IMMEDIATELY TURN AROUND AND SEND AN
// OUTGOING PACKET. Our scratch buffer is in use.
//
pPacketUse = (PS20DATAPACKET)m_ascTmpBuffer;
TRACE_OUT(( "Got data pkt type %u from [%d], compression %u", pPacket->data.dataType, pasPerson->mcsID, pPacket->data.compressionType));
//
// If the packet has CT_OLD_COMPRESSED set, it has used simple PKZIP
// compression
//
if (pPacket->data.compressionType & CT_OLD_COMPRESSED) { compression = CT_PKZIP; } else { //
// If the packet has any other type of compression, the algorithm used
// depends on the level of compression supported by the sender.
// - If only level 1 (NM10) compression is supported, this packet is
// uncompressed.
// - If level 2 (NM20) compression is supported, the packet is
// compressed, and the compression type is given in the packet header.
//
if (!pasPerson->cpcCaps.general.genCompressionLevel) { compression = 0; } else { compression = pPacket->data.compressionType; } }
TRACE_OUT(( "packet compressed with algorithm %u", compression));
//
// If the packet is compressed, decompress it now
//
if (compression) { PGDC_DICTIONARY pgdcDict = NULL;
//
// Copy the uncompressed packet header into the buffer.
//
memcpy(pPacketUse, pPacket, sizeof(*pPacket));
cbBufferSize = TSHR_MAX_SEND_PKT - sizeof(*pPacket);
if (compression == CT_PERSIST_PKZIP) { //
// Figure out what dictionary to use based on stream priority
//
switch (pPacket->stream) { case PROT_STR_UPDATES: dictionary = GDC_DICT_UPDATES; break;
case PROT_STR_MISC: dictionary = GDC_DICT_MISC; break;
case PROT_STR_INPUT: dictionary = GDC_DICT_INPUT; break;
default: ERROR_OUT(("Unrecognized stream ID")); break; }
pgdcDict = pasPerson->adcsDict + dictionary; } else if (compression != CT_PKZIP) { //
// If this isn't PKZIP or PERSIST_PKZIP, we don't know what
// it is (corrupted packet or incompatible T.128. Bail
// out now.
//
WARNING_OUT(("SC_ReceivedPacket: ignoring packet, don't recognize compression type")); DC_QUIT; }
//
// Decompress the data following the packet header.
// This should never fail - if it does, the data has probably been
// corrupted.
//
decompressed = GDC_Decompress(pgdcDict, m_agdcWorkBuf, (LPBYTE)(pPacket + 1), pPacket->data.compressedLength - sizeof(DATAPACKETHEADER), (LPBYTE)(pPacketUse + 1), &cbBufferSize);
if (!decompressed) { ERROR_OUT(( "Failed to decompress packet from [%d]!", pasPerson->mcsID)); DC_QUIT; } } else { // We have received an uncompressed buffer. Since we may modify the
// buffer's contents and what we have is an MCS buffer that may be
// sent to other nodes, we copy the data.
memcpy(pPacketUse, pPacket, pPacket->dataLength + sizeof(S20DATAPACKET) - sizeof(DATAPACKETHEADER)); }
// The packet (pPacketUse) is now decompressed
//
// ROUTE the packet
//
TRACE_OUT(("SC_ReceivedPacket: Received packet type %04d size %04d from [%d]", pPacketUse->data.dataType, pPacketUse->data.compressedLength, pPacketUse->header.user));
switch (pPacketUse->data.dataType) { case DT_CA: CA_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_CA30: CA30_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_IM: IM_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_SWL: SWL_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_HET: case DT_HET30: HET_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_UP: UP_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_FH: FH_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_CM: CM_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_CPC: CPC_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_AWC: AWC_ReceivedPacket(pasPerson, pPacketUse); break;
case DT_UNUSED_DS: TRACE_OUT(("Ignoring DS packet received from NM 2.x node")); break;
case DT_UNUSED_USR_FH_11: // Old R.11 FH packets
case DT_UNUSED_USR_FH_10: // Old R.10 FH packets
case DT_UNUSED_HCA: // Old High-Level Control Arbiter
case DT_UNUSED_SC: // Old R.11 SC packets
default: ERROR_OUT(( "Invalid packet received %u", pPacketUse->data.dataType)); break; } }
DC_EXIT_POINT: DebugExitVOID(ASShare::SC_ReceivedPacket); }
//
// SC_ShareStarting()
// Share initialization
//
// This in turn calls other component share starting
//
BOOL ASShare::SC_ShareStarting(void) { BOOL rc = FALSE; BOOL fViewSelf;
DebugEntry(ASShare::SC_ShareStarting);
//
// SC specific init
//
// Find out whether to view own shared stuff (a handy debugging tool)
COM_ReadProfInt(DBG_INI_SECTION_NAME, VIEW_INI_VIEWSELF, FALSE, &fViewSelf); m_scfViewSelf = (fViewSelf != FALSE);
// Create scratch compression buffer for outgoing/incoming packets
m_ascTmpBuffer = new BYTE[TSHR_MAX_SEND_PKT]; if (!m_ascTmpBuffer) { ERROR_OUT(("SC_Init: couldn't allocate m_ascTmpBuffer")); DC_QUIT; }
// Share version
m_scShareVersion = CAPS_VERSION_CURRENT;
//
// Component inits
//
if (!BCD_ShareStarting()) { ERROR_OUT(("BCD_ShareStarting failed")); DC_QUIT; }
if (!IM_ShareStarting()) { ERROR_OUT(("IM_ShareStarting failed")); DC_QUIT; }
if (!CM_ShareStarting()) { ERROR_OUT(("CM_ShareStarting failed")); DC_QUIT; }
if (!USR_ShareStarting()) { ERROR_OUT(("USR_ShareStarting failed")); DC_QUIT; }
if (!VIEW_ShareStarting()) { ERROR_OUT(("VIEW_ShareStarting failed")); DC_QUIT; }
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(ASShare::SC_ShareStarting, rc); return(rc); }
//
// SC_ShareEnded()
// Share cleanup
//
// This in turn calls other component share ended routines
//
void ASShare::SC_ShareEnded(void) { DebugEntry(ASShare::SC_ShareEnded);
//
// Delete remotes from share
//
if (m_pasLocal) { while (m_pasLocal->pasNext) { SC_PartyDeleted(m_pasLocal->pasNext->mcsID); }
//
// Delete local self from share
//
SC_PartyDeleted(m_pasLocal->mcsID); }
//
// Component Notifications
//
VIEW_ShareEnded();
USR_ShareEnded();
CM_ShareEnded();
IM_ShareEnded();
BCD_ShareEnded();
//
// SC specific term
//
// Free scratch buffer
if (m_ascTmpBuffer) { delete[] m_ascTmpBuffer; m_ascTmpBuffer = NULL; }
DebugExitVOID(ASShare::SC_ShareEnded); }
//
// SC_PartyJoiningShare()
//
// Called when a new party is joining the share. This is an internal
// function because it is the SC which calls all these functions. The
// processing done here relies on the capabilities - so it is in here as
// this is called after CPC_PartyJoiningShare.
//
// RETURNS:
//
// TRUE if the party can join the share.
//
// FALSE if the party can NOT join the share.
//
//
ASPerson * ASShare::SC_PartyJoiningShare ( UINT mcsID, LPSTR szName, UINT cbCaps, LPVOID pCaps ) { BOOL rc = FALSE; ASPerson * pasPerson = NULL;
// DebugEntry(ASShare::SC_PartyJoiningShare);
//
// Allocate an ASPerson for this dude. If this is the local person,
// it gets stuck at the front. Otherwise it gets stuck just past
// the front.
//
pasPerson = SC_PersonAllocate(mcsID, szName); if (!pasPerson) { ERROR_OUT(("SC_PartyAdded: can't allocate ASPerson for [%d]", mcsID)); DC_QUIT; }
//
// DO THIS FIRST -- we need the person's caps set up
//
if (!CPC_PartyJoiningShare(pasPerson, cbCaps, pCaps)) { ERROR_OUT(("CPC_PartyJoiningShare failed for [%d]", pasPerson->mcsID)); DC_QUIT; }
//
// Call the component joined routines.
//
if (!DCS_PartyJoiningShare(pasPerson)) { ERROR_OUT(("DCS_PartyJoiningShare failed for [%d]", pasPerson->mcsID)); DC_QUIT; }
if (!CM_PartyJoiningShare(pasPerson)) { ERROR_OUT(("CM_PartyJoiningShare failed for [%d]", pasPerson->mcsID)); DC_QUIT; }
if (!HET_PartyJoiningShare(pasPerson)) { ERROR_OUT(("HET_PartyJoiningShare failed for [%d]", pasPerson->mcsID)); DC_QUIT; }
//
// NOW THE PERSON IS JOINED.
// Recalculate capabilities of the share with this new person.
//
SC_RecalcCaps(TRUE);
rc = TRUE;
DC_EXIT_POINT: if (!rc) { //
// Don't worry, this person object will still be cleaned up,
// code at a higher layer will free by using the MCSID.
//
pasPerson = NULL; } // DebugExitPVOID(ASShare::SC_PartyJoiningShare, pasPerson);
return(pasPerson); }
//
// SC_RecalcCaps()
//
// Recalculates share capabilities after somebody has joined or left the
// share.
//
void ASShare::SC_RecalcCaps(BOOL fJoiner) { ASPerson * pasT;
DebugEntry(ASShare::SC_RecalcCaps);
//
// DO THIS FIRST -- recalculate the share version.
//
ValidatePerson(m_pasLocal); m_scShareVersion = m_pasLocal->cpcCaps.general.version;
for (pasT = m_pasLocal->pasNext; pasT != NULL; pasT = pasT->pasNext) { ValidatePerson(pasT); m_scShareVersion = min(m_scShareVersion, pasT->cpcCaps.general.version); }
//
// Do viewing & hosting stuff first
//
DCS_RecalcCaps(fJoiner); OE_RecalcCaps(fJoiner);
//
// Do hosting stuff second
//
USR_RecalcCaps(fJoiner); CM_RecalcCaps(fJoiner); PM_RecalcCaps(fJoiner); SBC_RecalcCaps(fJoiner); SSI_RecalcCaps(fJoiner);
DebugExitVOID(ASShare::SC_RecalcCaps); }
//
// FUNCTION: SC_PartyLeftShare()
//
// DESCRIPTION:
//
// Called when a party has left the share.
//
//
void ASShare::SC_PartyLeftShare(UINT_PTR mcsID) { ASPerson * pasPerson; ASPerson * pasT;
DebugEntry(SC_PartyLeftShare);
if (!SC_ValidateNetID(mcsID, &pasPerson)) { TRACE_OUT(("Couldn't find ASPerson for [%d]", mcsID)); DC_QUIT; }
// Tell the UI this dude is gone
if (!pasPerson->cpcCaps.share.gccID) { WARNING_OUT(("Skipping PartyLeftShare for person [%d], no GCC id", pasPerson->mcsID)); DC_QUIT; }
DCS_NotifyUI(SH_EVT_PERSON_LEFT, pasPerson->cpcCaps.share.gccID, 0);
//
// Tell everybody this person is gone.
//
//
// Notes on order of PartyLeftShare calls
//
// 1. HET must be called first, since that halts any sharing from this
// person, before we kick the person out of the share.
//
// 2. CA must be called before IM (as CA calls IM functions)
//
//
// This will stop hosting early
HET_PartyLeftShare(pasPerson);
CA_PartyLeftShare(pasPerson); CM_PartyLeftShare(pasPerson);
SWL_PartyLeftShare(pasPerson); VIEW_PartyLeftShare(pasPerson);
PM_PartyLeftShare(pasPerson); RBC_PartyLeftShare(pasPerson); OD2_PartyLeftShare(pasPerson);
OE_PartyLeftShare(pasPerson); DCS_PartyLeftShare(pasPerson);
//
// Free the person
//
SC_PersonFree(pasPerson);
//
// Recalculate the caps with him gone. But there's no point in doing
// this if it's the local dude, since the share will exit imminently.
//
if (m_pasLocal) { SC_RecalcCaps(FALSE); }
DC_EXIT_POINT: DebugExitVOID(ASShare::SC_PartyLeftShare); }
//
// FUNCTION: SCCheckForCMCall
//
// DESCRIPTION:
//
// This is called when we want to check if a CM call now exists (and do
// whatever is appropriate to join it etc).
//
// PARAMETERS: NONE
//
// RETURNS: TRUE if success; otherwise, FALSE.
//
//
void SCCheckForCMCall(void) { CM_STATUS cmStatus;
DebugEntry(SCCheckForCMCall);
ASSERT(g_asSession.scState == SCS_INIT);
//
// See if a call already exists
//
if (!g_asSession.callID) { if (CMS_GetStatus(&cmStatus)) { //
// The AS lock protects the g_asSession fields.
//
TRACE_OUT(("AS LOCK: SCCheckForCMCall")); UT_Lock(UTLOCK_AS);
g_asSession.callID = cmStatus.callID;
g_asSession.attendeePermissions = cmStatus.attendeePermissions; WARNING_OUT(("Local mtg settings 0x%08lx", g_asSession.attendeePermissions));
//
// This is the time to update our local person name. It's
// on our thread, but before the control packets exchange it
//
lstrcpy(g_asSession.achLocalName, cmStatus.localName); g_asSession.cchLocalName = lstrlen(g_asSession.achLocalName); TRACE_OUT(("Local Name is %s", g_asSession.achLocalName));
g_asSession.gccID = cmStatus.localHandle;
UT_Unlock(UTLOCK_AS); TRACE_OUT(("AS UNLOCK: SCCheckForCMCall")); } }
if (g_asSession.callID) { SC_CreateShare(S20_JOIN); }
DebugExitVOID(SCCheckForCMCall); }
#ifdef _DEBUG
void ASShare::ValidatePerson(ASPerson * pasPerson) { ASSERT(!IsBadWritePtr(pasPerson, sizeof(ASPerson))); ASSERT(!lstrcmp(pasPerson->stamp.idStamp, "ASPerso")); ASSERT(pasPerson->mcsID != MCSID_NULL); }
void ASShare::ValidateView(ASPerson * pasPerson) { ValidatePerson(pasPerson); ASSERT(!IsBadWritePtr(pasPerson->m_pView, sizeof(ASView))); ASSERT(!lstrcmp(pasPerson->m_pView->stamp.idStamp, "ASVIEW")); }
#endif // _DEBUG
//
// SC_PersonAllocate()
// This allocates a new ASPerson structure, fills in the debug/mcsID fields,
// and links it into the people-in-the-conference list.
//
// Eventually, all the PartyJoiningShare routines that simply init a field
// should go away and that info put here.
//
ASPerson * ASShare::SC_PersonAllocate(UINT mcsID, LPSTR szName) { ASPerson * pasNew;
// DebugEntry(ASShare::SC_PersonAllocate);
pasNew = new ASPerson; if (!pasNew) { ERROR_OUT(("Unable to allocate a new ASPerson")); DC_QUIT; } ZeroMemory(pasNew, sizeof(*pasNew)); SET_STAMP(pasNew, Person);
//
// Set up mcsID and name
//
pasNew->mcsID = mcsID; lstrcpyn(pasNew->scName, szName, TSHR_MAX_PERSON_NAME_LEN);
UT_Lock(UTLOCK_AS);
//
// Is this the local person?
//
if (!m_pasLocal) { m_pasLocal = pasNew; } else { UINT streamID;
//
// This is a remote. Set up the sync status right away in case
// the join process fails in the middle. Cleaning up will undo
// this always.
//
//
// Mark this person's streams as needing a sync from us before we
// can send data to him
// And and that we need a sync from him on each stream before we'll
// receive data from him
//
for (streamID = SC_STREAM_LOW; streamID <= SC_STREAM_HIGH; streamID++ ) { //
// Set up the sync status.
//
ASSERT(pasNew->scSyncSendStatus[streamID-1] == SC_NOT_SYNCED); ASSERT(pasNew->scSyncRecStatus[streamID-1] == SC_NOT_SYNCED); m_ascSynced[streamID-1]++; }
//
// Link into list
//
pasNew->pasNext = m_pasLocal->pasNext; m_pasLocal->pasNext = pasNew; }
UT_Unlock(UTLOCK_AS);
DC_EXIT_POINT: // DebugExitPVOID(ASShare::SC_PersonAllocate, pasNew);
return(pasNew); }
//
// SC_PersonFree()
// This gets a person out of the linked list and frees the memory for them.
//
void ASShare::SC_PersonFree(ASPerson * pasFree) { ASPerson ** ppasPerson; UINT streamID;
DebugEntry(ASShare::SC_PersonFree);
ValidatePerson(pasFree);
for (ppasPerson = &m_pasLocal; *(ppasPerson) != NULL; ppasPerson = &((*ppasPerson)->pasNext)) { if ((*ppasPerson) == pasFree) { //
// Found it.
//
TRACE_OUT(("SC_PersonUnhook: unhooking person [%d]", pasFree->mcsID));
if (pasFree == m_pasLocal) { ASSERT(pasFree->pasNext == NULL); } else { //
// Clear syncs
//
// If this person was never synced, subtract them from the
// global "needing sync" count on each stream
//
for (streamID = SC_STREAM_LOW; streamID <= SC_STREAM_HIGH; streamID++ ) { if (pasFree->scSyncSendStatus[streamID-1] == SC_NOT_SYNCED) { ASSERT(m_ascSynced[streamID-1] > 0); m_ascSynced[streamID-1]--; } } }
UT_Lock(UTLOCK_AS);
//
// Fix up linked list.
//
(*ppasPerson) = pasFree->pasNext;
#ifdef _DEBUG
ZeroMemory(pasFree, sizeof(ASPerson)); #endif // _DEBUG
delete pasFree;
UT_Unlock(UTLOCK_AS); DC_QUIT; } }
//
// We didn't find this guy in the list--this is very bad.
//
ERROR_OUT(("SC_PersonFree: didn't find person %d", pasFree));
DC_EXIT_POINT: DebugExitVOID(ASShare::SC_PersonFree); }
//
// SC_AllocPkt()
// Allocates a SEND packet
//
PS20DATAPACKET ASShare::SC_AllocPkt ( UINT streamID, UINT_PTR nodeID, UINT_PTR cbSizePkt ) { PS20DATAPACKET pPacket = NULL;
// DebugEntry(ASShare::SC_AllocPkt);
if (g_asSession.scState != SCS_SHARING) { TRACE_OUT(("SC_AllocPkt failed; share is ending")); DC_QUIT; }
ASSERT((streamID >= SC_STREAM_LOW) && (streamID <= SC_STREAM_HIGH)); ASSERT(cbSizePkt >= sizeof(S20DATAPACKET));
//
// We'd better not be in the middle of a sync!
//
ASSERT(!m_scfInSync);
//
// Try and send any outstanding syncs
//
if (!SCSyncStream(streamID)) { //
// If the stream is still not synced, don't allocate the packet
//
WARNING_OUT(("SC_AllocPkt failed; outstanding syncs are present")); DC_QUIT; }
pPacket = S20_AllocDataPkt(streamID, nodeID, cbSizePkt);
DC_EXIT_POINT: // DebugExitPVOID(ASShare::SC_AllocPkt, pPacket);
return(pPacket); }
//
// SCSyncStream()
//
// This broadcasts a SNI sync packet intended for a new person who has just
// joined the share. That person ignores all received data from us until
// they get the sync. That's because data in transit before they are synced
// could refer to PKZIP data they don't have, second level order encoding
// info they can't decode, orders they can't process, etc.
//
// When we receive a SYNC from a remote, we then know that following
// data from that remote will make sense. The remote has settled us into
// the share, and the data encorporates our capabilities and will not refer
// to prior state info from before we joined the share.
//
// NOTE that in 2.x, this was O(N^2) where N is the number of people now in
// the share! Each person in the share would send SNI sync packets for each
// stream for each other person in the share, even for people who weren't new.
// But those people wouldn't reset received state, and would (could) continue
// processing data from us. When they finally got their sync packet, it
// would do nothing! Even worst, 2 of the 5 streams are never used,
// and one stream is only used when a person is hosting. So 3 of these 5
// O(N^2) broadcasts were useless all or the majority of the time.
//
// So now we only send SNI sync packets for new joiners. This makes joining
// an O(N) broadcast algorithm.
//
// LAURABU BOGUS
// Post Beta1, we can make this even better. Each broadcast is itself O(N)
// packets. So for beta1, joining/syncing is O(N^2) packets instead of
// O(N^3) packets. With targeted sends (not broadcasts) whenever possible,
// we can drop this to O(N) total packets.
//
// NOTE also that no real app sharing packets are sent on a stream until
// we have fully synced everybody. That means we've reduced a lot the
// lag between somebody dialing into a conference and their seeing a result,
// and everybody else being able to work again.
//
BOOL ASShare::SCSyncStream(UINT streamID) { ASPerson * pasPerson; PSNIPACKET pSNIPacket; BOOL rc = TRUE;
DebugEntry(ASShare::SCSyncStream);
//
// Loop through each person in the call broadcasting sync packets as
// necessary.
//
// LAURABU BOGUS
// We can change this to a targeted send after Beta 1.
//
//
// Note that new people are added to the front of the this. So we will
// bail out of this loop very quickly when sending syncs to newcomers.
//
ValidatePerson(m_pasLocal);
pasPerson = m_pasLocal->pasNext; while ((m_ascSynced[streamID-1] > 0) && (pasPerson != NULL)) { ValidatePerson(pasPerson);
//
// If this person is new, we need to send them a SYNC packet so
// they know we are done processing their join and know they
// are in the share.
//
if (pasPerson->scSyncSendStatus[streamID-1] != SC_SYNCED) { TRACE_OUT(("Syncing stream %d for person [%d] in broadcast way", streamID, pasPerson->mcsID));
//
// YES, syncs are broadcast even though they have the mcsID of
// just one person, the person they are intended for. Since we
// broadcast all state data, we also broadcast syncs. That's
// the only way to make sure the packets arrive in the same
// order, SYNC before data.
//
pSNIPacket = (PSNIPACKET)S20_AllocDataPkt(streamID, g_s20BroadcastID, sizeof(SNIPACKET)); if (!pSNIPacket) { TRACE_OUT(("Failed to alloc SNI sync packet")); rc = FALSE; break; }
//
// Set SNI packet fields
//
pSNIPacket->header.data.dataType = DT_SNI; pSNIPacket->message = SNI_MSG_SYNC; pSNIPacket->destination = (NET_UID)pasPerson->mcsID;
S20_SendDataPkt(streamID, g_s20BroadcastID, &(pSNIPacket->header));
pasPerson->scSyncSendStatus[streamID-1] = SC_SYNCED;
ASSERT(m_ascSynced[streamID-1] > 0); m_ascSynced[streamID-1]--; }
pasPerson = pasPerson->pasNext; }
DebugExitBOOL(ASShare::SCSyncStream, rc); return(rc); }
|