Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1595 lines
40 KiB

  1. #include "precomp.h"
  2. //
  3. // SC.CPP
  4. // Share Controller
  5. //
  6. // NOTE:
  7. // We must take the UTLOCK_AS every time we
  8. // * create/destroy the share object
  9. // * add/remove a person from the share
  10. //
  11. // Copyright(c) Microsoft 1997-
  12. //
  13. #define MLZ_FILE_ZONE ZONE_CORE
  14. //
  15. // SC_Init()
  16. // Initializes the share controller
  17. //
  18. BOOL SC_Init(void)
  19. {
  20. BOOL rc = FALSE;
  21. DebugEntry(SC_Init);
  22. ASSERT(!g_asSession.callID);
  23. ASSERT(!g_asSession.gccID);
  24. ASSERT(g_asSession.scState == SCS_TERM);
  25. //
  26. // Register as a Call Manager Secondary task
  27. //
  28. if (!CMS_Register(g_putAS, CMTASK_DCS, &g_pcmClientSc))
  29. {
  30. ERROR_OUT(( "Failed to register with CMS"));
  31. DC_QUIT;
  32. }
  33. g_asSession.scState = SCS_INIT;
  34. TRACE_OUT(("g_asSession.scState is SCS_INIT"));
  35. rc = TRUE;
  36. DC_EXIT_POINT:
  37. DebugExitBOOL(SC_Init, rc);
  38. return(rc);
  39. }
  40. //
  41. // SC_Term()
  42. //
  43. // See sc.h for description.
  44. //
  45. //
  46. void SC_Term(void)
  47. {
  48. DebugEntry(SC_Term);
  49. //
  50. // Clear up the core's state by generating appropriate PARTY_DELETED and
  51. // SHARE_ENDED events.
  52. //
  53. switch (g_asSession.scState)
  54. {
  55. case SCS_SHARING:
  56. case SCS_SHAREENDING:
  57. case SCS_SHAREPENDING:
  58. SC_End();
  59. break;
  60. }
  61. //
  62. // Deregister from the Call Manager
  63. //
  64. if (g_pcmClientSc)
  65. {
  66. CMS_Deregister(&g_pcmClientSc);
  67. }
  68. g_asSession.gccID = 0;
  69. g_asSession.callID = 0;
  70. g_asSession.attendeePermissions = NM_PERMIT_ALL;
  71. g_asSession.scState = SCS_TERM;
  72. TRACE_OUT(("g_asSession.scState is SCS_TERM"));
  73. DebugExitVOID(SC_Term);
  74. }
  75. //
  76. // SC_CreateShare()
  77. // Creates a new share or joins an existing one
  78. //
  79. BOOL SC_CreateShare(UINT s20CreateOrJoin)
  80. {
  81. BOOL rc = FALSE;
  82. DebugEntry(SC_CreateShare);
  83. //
  84. // If we are initialised but there is no Call Manager call, return the
  85. // race condition.
  86. //
  87. if ((g_asSession.scState != SCS_INIT) && (g_asSession.scState != SCS_SHAREPENDING))
  88. {
  89. TRACE_OUT(("Ignoring SC_CreateShare() request; in bad state %d",
  90. g_asSession.scState));
  91. DC_QUIT;
  92. }
  93. if (!g_asSession.callID)
  94. {
  95. WARNING_OUT(("Ignoring SC_CreateShare() request; not in T120 call"));
  96. DC_QUIT;
  97. }
  98. //
  99. // Remember that we created this share.
  100. //
  101. g_asSession.fShareCreator = (s20CreateOrJoin == S20_CREATE);
  102. TRACE_OUT(("CreatedShare is %s", (s20CreateOrJoin == S20_CREATE) ?
  103. "TRUE" : "FALSE"));
  104. g_asSession.scState = SCS_SHAREPENDING;
  105. TRACE_OUT(("g_asSession.scState is SCS_SHAREPENDING"));
  106. rc = S20CreateOrJoinShare(s20CreateOrJoin, g_asSession.callID);
  107. if (!rc)
  108. {
  109. WARNING_OUT(("%s failed", (s20CreateOrJoin == S20_CREATE ? "S20_CREATE" : "S20_JOIN")));
  110. }
  111. DC_EXIT_POINT:
  112. DebugExitBOOL(SC_CreateShare, rc);
  113. return(rc);
  114. }
  115. //
  116. // SC_EndShare()
  117. // This will end a share if we are in one or in the middle of establishing
  118. // one, and clean up afterwards.
  119. //
  120. void SC_EndShare(void)
  121. {
  122. DebugEntry(SC_EndShare);
  123. if (g_asSession.scState <= SCS_SHAREENDING)
  124. {
  125. TRACE_OUT(("Ignoring SC_EndShare(); nothing to do in state %d", g_asSession.scState));
  126. }
  127. else
  128. {
  129. //
  130. // If we are in a share or in the middle of creating/joining one, stop
  131. // the process.
  132. //
  133. //
  134. // Kill the share
  135. // NOTE that this will call SC_End(), when we come back
  136. // from this function our g_asSession.scState() should be SCS_INIT.
  137. //
  138. g_asSession.scState = SCS_SHAREENDING;
  139. TRACE_OUT(("g_asSession.scState is SCS_SHAREENDING"));
  140. S20LeaveOrEndShare();
  141. g_asSession.scState = SCS_INIT;
  142. }
  143. DebugExitVOID(SC_EndShare);
  144. }
  145. //
  146. // SC_PersonFromNetID()
  147. //
  148. ASPerson * ASShare::SC_PersonFromNetID(UINT_PTR mcsID)
  149. {
  150. ASPerson * pasPerson;
  151. DebugEntry(SC_PersonFromNetID);
  152. ASSERT(mcsID != MCSID_NULL);
  153. //
  154. // Search for the mcsID.
  155. //
  156. if (!SC_ValidateNetID(mcsID, &pasPerson))
  157. {
  158. ERROR_OUT(("Invalid [%u]", mcsID));
  159. }
  160. DebugExitPVOID(ASShare::SC_PersonFromNetID, pasPerson);
  161. return(pasPerson);
  162. }
  163. //
  164. // SC_ValidateNetID()
  165. //
  166. BOOL ASShare::SC_ValidateNetID
  167. (
  168. UINT_PTR mcsID,
  169. ASPerson * * ppasPerson
  170. )
  171. {
  172. BOOL rc = FALSE;
  173. ASPerson * pasPerson;
  174. DebugEntry(ASShare::SC_ValidateNetID);
  175. // Init to empty
  176. pasPerson = NULL;
  177. //
  178. // MCSID_NULL matches no one.
  179. //
  180. if (mcsID == MCSID_NULL)
  181. {
  182. WARNING_OUT(("SC_ValidateNetID called with MCSID_NULL"));
  183. DC_QUIT;
  184. }
  185. //
  186. // Search for the mcsID.
  187. //
  188. for (pasPerson = m_pasLocal; pasPerson != NULL; pasPerson = pasPerson->pasNext)
  189. {
  190. ValidatePerson(pasPerson);
  191. if (pasPerson->mcsID == mcsID)
  192. {
  193. //
  194. // Found required person, set return values and quit
  195. //
  196. rc = TRUE;
  197. break;
  198. }
  199. }
  200. DC_EXIT_POINT:
  201. if (ppasPerson)
  202. {
  203. *ppasPerson = pasPerson;
  204. }
  205. DebugExitBOOL(ASShare::SC_ValidateNetID, rc);
  206. return(rc);
  207. }
  208. //
  209. // SC_PersonFromGccID()
  210. //
  211. ASPerson * ASShare::SC_PersonFromGccID(UINT gccID)
  212. {
  213. ASPerson * pasPerson;
  214. // DebugEntry(ASShare::SC_PersonFromGccID);
  215. for (pasPerson = m_pasLocal; pasPerson != NULL; pasPerson = pasPerson->pasNext)
  216. {
  217. ValidatePerson(pasPerson);
  218. if (pasPerson->cpcCaps.share.gccID == gccID)
  219. {
  220. // Found it
  221. break;
  222. }
  223. }
  224. // DebugExitPVOID(ASShare::SC_PersonFromGccID, pasPerson);
  225. return(pasPerson);
  226. }
  227. //
  228. // SC_Start()
  229. // Inits the share (if all is OK), and adds local person to it.
  230. //
  231. BOOL SC_Start(UINT mcsID)
  232. {
  233. BOOL rc = FALSE;
  234. ASShare * pShare;
  235. ASPerson * pasPerson;
  236. DebugEntry(SC_Start);
  237. ASSERT(g_asSession.callID);
  238. ASSERT(g_asSession.gccID);
  239. if ((g_asSession.scState != SCS_INIT) && (g_asSession.scState != SCS_SHAREPENDING))
  240. {
  241. WARNING_OUT(("Ignoring SC_Start(); in bad state"));
  242. DC_QUIT;
  243. }
  244. if (g_asSession.pShare)
  245. {
  246. WARNING_OUT(("Ignoring SC_Start(); have ASShare object already"));
  247. DC_QUIT;
  248. }
  249. g_asSession.scState = SCS_SHARING;
  250. TRACE_OUT(("g_asSession.scState is SCS_SHARING"));
  251. #ifdef _DEBUG
  252. //
  253. // Use this to calculate time between joining share and getting
  254. // first view
  255. //
  256. g_asSession.scShareTime = ::GetTickCount();
  257. #endif // _DEBUG
  258. //
  259. // Allocate the share object and add the local dude to the share.
  260. //
  261. pShare = new ASShare;
  262. if (pShare)
  263. {
  264. ZeroMemory(pShare, sizeof(*(pShare)));
  265. SET_STAMP(pShare, SHARE);
  266. rc = pShare->SC_ShareStarting();
  267. }
  268. UT_Lock(UTLOCK_AS);
  269. g_asSession.pShare = pShare;
  270. UT_Unlock(UTLOCK_AS);
  271. if (!rc)
  272. {
  273. ERROR_OUT(("Can't create/init ASShare"));
  274. DC_QUIT;
  275. }
  276. DCS_NotifyUI(SH_EVT_SHARE_STARTED, 0, 0);
  277. //
  278. // Join local dude into share. If that fails, also bail out.
  279. //
  280. pasPerson = g_asSession.pShare->SC_PartyJoiningShare(mcsID, g_asSession.achLocalName, sizeof(g_cpcLocalCaps), &g_cpcLocalCaps);
  281. if (!pasPerson)
  282. {
  283. ERROR_OUT(("Local person not joined into share successfully"));
  284. DC_QUIT;
  285. }
  286. //
  287. // Okay! We have a share, and it's set up.
  288. //
  289. //
  290. // Tell the UI that we're in the share.
  291. //
  292. DCS_NotifyUI(SH_EVT_PERSON_JOINED, pasPerson->cpcCaps.share.gccID, 0);
  293. //
  294. // Start periodic processing if ViewSelf or record/playback is on.
  295. //
  296. if (g_asSession.pShare->m_scfViewSelf)
  297. {
  298. SCH_ContinueScheduling(SCH_MODE_NORMAL);
  299. }
  300. rc = TRUE;
  301. DC_EXIT_POINT:
  302. DebugExitBOOL(SC_Start, rc);
  303. return(rc);
  304. }
  305. //
  306. // SC_End()
  307. // Removes any remotes from the share, removes the local person, and
  308. // cleans up after the fact.
  309. //
  310. void SC_End(void)
  311. {
  312. DebugEntry(SC_End);
  313. if (g_asSession.scState < SCS_SHAREENDING)
  314. {
  315. TRACE_OUT(("Ignoring SC_EVENT_SHAREENDED"));
  316. }
  317. else
  318. {
  319. if (g_asSession.pShare)
  320. {
  321. g_asSession.pShare->SC_ShareEnded();
  322. UT_Lock(UTLOCK_AS);
  323. delete g_asSession.pShare;
  324. g_asSession.pShare = NULL;
  325. UT_Unlock(UTLOCK_AS);
  326. DCS_NotifyUI(SH_EVT_SHARE_ENDED, 0, 0);
  327. }
  328. //
  329. // If the previous state was SCS_SHARE_PENDING then we
  330. // may have ended up here as the result of a 'back off' after
  331. // trying to create two shares. Let's try to join again...
  332. //
  333. if (g_asSession.fShareCreator)
  334. {
  335. g_asSession.fShareCreator = FALSE;
  336. TRACE_OUT(("CreatedShare is FALSE"));
  337. if (g_asSession.scState == SCS_SHAREPENDING)
  338. {
  339. WARNING_OUT(("Got share end while share pending - retry join"));
  340. UT_PostEvent(g_putAS, g_putAS, 0, CMS_NEW_CALL, 0, 0);
  341. }
  342. }
  343. g_asSession.scState = SCS_INIT;
  344. TRACE_OUT(("g_asSession.scState is SCS_INIT"));
  345. }
  346. g_s20ShareCorrelator = 0;
  347. //
  348. // Reset the queued control packets.
  349. //
  350. g_s20ControlPacketQHead = 0;
  351. g_s20ControlPacketQTail = 0;
  352. g_s20State = S20_NO_SHARE;
  353. TRACE_OUT(("g_s20State is S20_NO_SHARE"));
  354. DebugExitVOID(SC_End);
  355. }
  356. //
  357. // SC_PartyAdded()
  358. //
  359. BOOL ASShare::SC_PartyAdded
  360. (
  361. UINT mcsID,
  362. LPSTR szName,
  363. UINT cbCaps,
  364. LPVOID pCaps
  365. )
  366. {
  367. BOOL rc = FALSE;
  368. ASPerson * pasPerson;
  369. if (g_asSession.scState != SCS_SHARING)
  370. {
  371. WARNING_OUT(("Ignoring SC_EVENT_PARTYADDED; not in share"));
  372. DC_QUIT;
  373. }
  374. ASSERT(g_asSession.callID);
  375. ASSERT(g_asSession.gccID);
  376. //
  377. // A remote party is joining the share
  378. //
  379. //
  380. // Notify everybody
  381. //
  382. pasPerson = SC_PartyJoiningShare(mcsID, szName, cbCaps, pCaps);
  383. if (!pasPerson)
  384. {
  385. WARNING_OUT(("SC_PartyJoiningShare failed for remote [%d]", mcsID));
  386. DC_QUIT;
  387. }
  388. //
  389. // SYNC now
  390. // We should NEVER SEND ANY PACKETS when syncing. We aren't ready
  391. // because we haven't joined the person into the share. yet.
  392. // So we simply set variables for us to send data off the next
  393. // periodic schedule.
  394. //
  395. #ifdef _DEBUG
  396. m_scfInSync = TRUE;
  397. #endif // _DEBUG
  398. //
  399. // Stuff needed for being in the share, hosting or not
  400. //
  401. DCS_SyncOutgoing();
  402. OE_SyncOutgoing();
  403. if (m_pHost != NULL)
  404. {
  405. //
  406. // Common to both starting sharing and retransmitting sharing info
  407. //
  408. m_pHost->HET_SyncCommon();
  409. m_pHost->HET_SyncAlreadyHosting();
  410. m_pHost->CA_SyncAlreadyHosting();
  411. }
  412. #ifdef _DEBUG
  413. m_scfInSync = FALSE;
  414. #endif // _DEBUG
  415. //
  416. // DO THIS LAST -- tell the UI this person is in the share.
  417. //
  418. DCS_NotifyUI(SH_EVT_PERSON_JOINED, pasPerson->cpcCaps.share.gccID, 0);
  419. //
  420. // Start periodic processing
  421. //
  422. SCH_ContinueScheduling(SCH_MODE_NORMAL);
  423. rc = TRUE;
  424. DC_EXIT_POINT:
  425. DebugExitBOOL(ASShare::SC_PartyAdded, rc);
  426. return(rc);
  427. }
  428. //
  429. // SC_PartyDeleted()
  430. //
  431. void ASShare::SC_PartyDeleted(UINT_PTR mcsID)
  432. {
  433. if ((g_asSession.scState != SCS_SHARING) && (g_asSession.scState != SCS_SHAREENDING))
  434. {
  435. WARNING_OUT(("Ignoring SC_EVENT_PARTYDELETED; wrong state"));
  436. DC_QUIT;
  437. }
  438. SC_PartyLeftShare(mcsID);
  439. DC_EXIT_POINT:
  440. DebugExitVOID(ASShare::SC_PartyDeleted);
  441. }
  442. //
  443. // SC_ReceivedPacket()
  444. //
  445. void ASShare::SC_ReceivedPacket(PS20DATAPACKET pPacket)
  446. {
  447. ASPerson * pasPerson;
  448. PSNIPACKET pSNIPacket;
  449. DebugEntry(ASShare::SC_ReceivedPacket);
  450. if (g_asSession.scState != SCS_SHARING)
  451. {
  452. WARNING_OUT(("Ignoring received data because we're not in right state"));
  453. DC_QUIT;
  454. }
  455. //
  456. // Ignore packets on streams we don't know about.
  457. //
  458. if ((pPacket->stream < SC_STREAM_LOW) ||
  459. (pPacket->stream > SC_STREAM_HIGH))
  460. {
  461. TRACE_OUT(("Ignoring received data on unrecognized stream %d",
  462. pPacket->stream));
  463. DC_QUIT;
  464. }
  465. //
  466. // It is possible to get a packet from a person we do not know
  467. // about.
  468. //
  469. // This can happen if we join a conference that has an existing
  470. // share session. All existing parties will be sending data on
  471. // the channels we have joined but we will not yet have
  472. // received the events to add them to our share session.
  473. //
  474. // Data packets from unknown people are ignored (ignoring this
  475. // data is OK as we will resync with them when they are added
  476. // to our share session)
  477. //
  478. if (!SC_ValidateNetID(pPacket->header.user, &pasPerson))
  479. {
  480. WARNING_OUT(("Ignoring data packet from unknown person [%d]",
  481. pPacket->header.user));
  482. DC_QUIT;
  483. }
  484. if (pPacket->data.dataType == DT_SNI)
  485. {
  486. //
  487. // This is an SNI packet - handle it here.
  488. //
  489. pSNIPacket = (PSNIPACKET)pPacket;
  490. switch(pSNIPacket->message)
  491. {
  492. case SNI_MSG_SYNC:
  493. //
  494. // This is a sync message.
  495. //
  496. if (pSNIPacket->destination == m_pasLocal->mcsID)
  497. {
  498. //
  499. // This sync message is for us.
  500. //
  501. pasPerson->scSyncRecStatus[pPacket->stream-1] = SC_SYNCED;
  502. }
  503. else
  504. {
  505. TRACE_OUT(("Ignoring SYNC on stream %d for [%d] from [%d]",
  506. pPacket->stream, pSNIPacket->destination,
  507. pPacket->header.user));
  508. }
  509. break;
  510. default:
  511. ERROR_OUT(("Unknown SNI message %u", pSNIPacket->message));
  512. break;
  513. }
  514. }
  515. else if (pasPerson->scSyncRecStatus[pPacket->stream-1] == SC_SYNCED)
  516. {
  517. PS20DATAPACKET pPacketUse;
  518. UINT cbBufferSize;
  519. UINT compression;
  520. BOOL decompressed;
  521. UINT dictionary;
  522. //
  523. // Decompress the packet if necessary
  524. //
  525. //
  526. // Use the temporary buffer. This will never fail, so we don't
  527. // need to check the return value. THIS MEANS THAT THE HANDLING OF
  528. // INCOMING PACKETS CAN NEVER IMMEDIATELY TURN AROUND AND SEND AN
  529. // OUTGOING PACKET. Our scratch buffer is in use.
  530. //
  531. pPacketUse = (PS20DATAPACKET)m_ascTmpBuffer;
  532. TRACE_OUT(( "Got data pkt type %u from [%d], compression %u",
  533. pPacket->data.dataType, pasPerson->mcsID,
  534. pPacket->data.compressionType));
  535. //
  536. // If the packet has CT_OLD_COMPRESSED set, it has used simple PKZIP
  537. // compression
  538. //
  539. if (pPacket->data.compressionType & CT_OLD_COMPRESSED)
  540. {
  541. compression = CT_PKZIP;
  542. }
  543. else
  544. {
  545. //
  546. // If the packet has any other type of compression, the algorithm used
  547. // depends on the level of compression supported by the sender.
  548. // - If only level 1 (NM10) compression is supported, this packet is
  549. // uncompressed.
  550. // - If level 2 (NM20) compression is supported, the packet is
  551. // compressed, and the compression type is given in the packet header.
  552. //
  553. if (!pasPerson->cpcCaps.general.genCompressionLevel)
  554. {
  555. compression = 0;
  556. }
  557. else
  558. {
  559. compression = pPacket->data.compressionType;
  560. }
  561. }
  562. TRACE_OUT(( "packet compressed with algorithm %u", compression));
  563. //
  564. // If the packet is compressed, decompress it now
  565. //
  566. if (compression)
  567. {
  568. PGDC_DICTIONARY pgdcDict = NULL;
  569. //
  570. // Copy the uncompressed packet header into the buffer.
  571. //
  572. memcpy(pPacketUse, pPacket, sizeof(*pPacket));
  573. cbBufferSize = TSHR_MAX_SEND_PKT - sizeof(*pPacket);
  574. if (compression == CT_PERSIST_PKZIP)
  575. {
  576. //
  577. // Figure out what dictionary to use based on stream priority
  578. //
  579. switch (pPacket->stream)
  580. {
  581. case PROT_STR_UPDATES:
  582. dictionary = GDC_DICT_UPDATES;
  583. break;
  584. case PROT_STR_MISC:
  585. dictionary = GDC_DICT_MISC;
  586. break;
  587. case PROT_STR_INPUT:
  588. dictionary = GDC_DICT_INPUT;
  589. break;
  590. default:
  591. ERROR_OUT(("Unrecognized stream ID"));
  592. break;
  593. }
  594. pgdcDict = pasPerson->adcsDict + dictionary;
  595. }
  596. else if (compression != CT_PKZIP)
  597. {
  598. //
  599. // If this isn't PKZIP or PERSIST_PKZIP, we don't know what
  600. // it is (corrupted packet or incompatible T.128. Bail
  601. // out now.
  602. //
  603. WARNING_OUT(("SC_ReceivedPacket: ignoring packet, don't recognize compression type"));
  604. DC_QUIT;
  605. }
  606. //
  607. // Decompress the data following the packet header.
  608. // This should never fail - if it does, the data has probably been
  609. // corrupted.
  610. //
  611. decompressed = GDC_Decompress(pgdcDict, m_agdcWorkBuf,
  612. (LPBYTE)(pPacket + 1),
  613. pPacket->data.compressedLength - sizeof(DATAPACKETHEADER),
  614. (LPBYTE)(pPacketUse + 1),
  615. &cbBufferSize);
  616. if (!decompressed)
  617. {
  618. ERROR_OUT(( "Failed to decompress packet from [%d]!", pasPerson->mcsID));
  619. DC_QUIT;
  620. }
  621. }
  622. else
  623. {
  624. // We have received an uncompressed buffer. Since we may modify the
  625. // buffer's contents and what we have is an MCS buffer that may be
  626. // sent to other nodes, we copy the data.
  627. memcpy(pPacketUse, pPacket, pPacket->dataLength + sizeof(S20DATAPACKET)
  628. - sizeof(DATAPACKETHEADER));
  629. }
  630. // The packet (pPacketUse) is now decompressed
  631. //
  632. // ROUTE the packet
  633. //
  634. TRACE_OUT(("SC_ReceivedPacket: Received packet type %04d size %04d from [%d]",
  635. pPacketUse->data.dataType, pPacketUse->data.compressedLength,
  636. pPacketUse->header.user));
  637. switch (pPacketUse->data.dataType)
  638. {
  639. case DT_CA:
  640. CA_ReceivedPacket(pasPerson, pPacketUse);
  641. break;
  642. case DT_CA30:
  643. CA30_ReceivedPacket(pasPerson, pPacketUse);
  644. break;
  645. case DT_IM:
  646. IM_ReceivedPacket(pasPerson, pPacketUse);
  647. break;
  648. case DT_SWL:
  649. SWL_ReceivedPacket(pasPerson, pPacketUse);
  650. break;
  651. case DT_HET:
  652. case DT_HET30:
  653. HET_ReceivedPacket(pasPerson, pPacketUse);
  654. break;
  655. case DT_UP:
  656. UP_ReceivedPacket(pasPerson, pPacketUse);
  657. break;
  658. case DT_FH:
  659. FH_ReceivedPacket(pasPerson, pPacketUse);
  660. break;
  661. case DT_CM:
  662. CM_ReceivedPacket(pasPerson, pPacketUse);
  663. break;
  664. case DT_CPC:
  665. CPC_ReceivedPacket(pasPerson, pPacketUse);
  666. break;
  667. case DT_AWC:
  668. AWC_ReceivedPacket(pasPerson, pPacketUse);
  669. break;
  670. case DT_UNUSED_DS:
  671. TRACE_OUT(("Ignoring DS packet received from NM 2.x node"));
  672. break;
  673. case DT_UNUSED_USR_FH_11: // Old R.11 FH packets
  674. case DT_UNUSED_USR_FH_10: // Old R.10 FH packets
  675. case DT_UNUSED_HCA: // Old High-Level Control Arbiter
  676. case DT_UNUSED_SC: // Old R.11 SC packets
  677. default:
  678. ERROR_OUT(( "Invalid packet received %u",
  679. pPacketUse->data.dataType));
  680. break;
  681. }
  682. }
  683. DC_EXIT_POINT:
  684. DebugExitVOID(ASShare::SC_ReceivedPacket);
  685. }
  686. //
  687. // SC_ShareStarting()
  688. // Share initialization
  689. //
  690. // This in turn calls other component share starting
  691. //
  692. BOOL ASShare::SC_ShareStarting(void)
  693. {
  694. BOOL rc = FALSE;
  695. BOOL fViewSelf;
  696. DebugEntry(ASShare::SC_ShareStarting);
  697. //
  698. // SC specific init
  699. //
  700. // Find out whether to view own shared stuff (a handy debugging tool)
  701. COM_ReadProfInt(DBG_INI_SECTION_NAME, VIEW_INI_VIEWSELF, FALSE, &fViewSelf);
  702. m_scfViewSelf = (fViewSelf != FALSE);
  703. // Create scratch compression buffer for outgoing/incoming packets
  704. m_ascTmpBuffer = new BYTE[TSHR_MAX_SEND_PKT];
  705. if (!m_ascTmpBuffer)
  706. {
  707. ERROR_OUT(("SC_Init: couldn't allocate m_ascTmpBuffer"));
  708. DC_QUIT;
  709. }
  710. // Share version
  711. m_scShareVersion = CAPS_VERSION_CURRENT;
  712. //
  713. // Component inits
  714. //
  715. if (!BCD_ShareStarting())
  716. {
  717. ERROR_OUT(("BCD_ShareStarting failed"));
  718. DC_QUIT;
  719. }
  720. if (!IM_ShareStarting())
  721. {
  722. ERROR_OUT(("IM_ShareStarting failed"));
  723. DC_QUIT;
  724. }
  725. if (!CM_ShareStarting())
  726. {
  727. ERROR_OUT(("CM_ShareStarting failed"));
  728. DC_QUIT;
  729. }
  730. if (!USR_ShareStarting())
  731. {
  732. ERROR_OUT(("USR_ShareStarting failed"));
  733. DC_QUIT;
  734. }
  735. if (!VIEW_ShareStarting())
  736. {
  737. ERROR_OUT(("VIEW_ShareStarting failed"));
  738. DC_QUIT;
  739. }
  740. rc = TRUE;
  741. DC_EXIT_POINT:
  742. DebugExitBOOL(ASShare::SC_ShareStarting, rc);
  743. return(rc);
  744. }
  745. //
  746. // SC_ShareEnded()
  747. // Share cleanup
  748. //
  749. // This in turn calls other component share ended routines
  750. //
  751. void ASShare::SC_ShareEnded(void)
  752. {
  753. DebugEntry(ASShare::SC_ShareEnded);
  754. //
  755. // Delete remotes from share
  756. //
  757. if (m_pasLocal)
  758. {
  759. while (m_pasLocal->pasNext)
  760. {
  761. SC_PartyDeleted(m_pasLocal->pasNext->mcsID);
  762. }
  763. //
  764. // Delete local self from share
  765. //
  766. SC_PartyDeleted(m_pasLocal->mcsID);
  767. }
  768. //
  769. // Component Notifications
  770. //
  771. VIEW_ShareEnded();
  772. USR_ShareEnded();
  773. CM_ShareEnded();
  774. IM_ShareEnded();
  775. BCD_ShareEnded();
  776. //
  777. // SC specific term
  778. //
  779. // Free scratch buffer
  780. if (m_ascTmpBuffer)
  781. {
  782. delete[] m_ascTmpBuffer;
  783. m_ascTmpBuffer = NULL;
  784. }
  785. DebugExitVOID(ASShare::SC_ShareEnded);
  786. }
  787. //
  788. // SC_PartyJoiningShare()
  789. //
  790. // Called when a new party is joining the share. This is an internal
  791. // function because it is the SC which calls all these functions. The
  792. // processing done here relies on the capabilities - so it is in here as
  793. // this is called after CPC_PartyJoiningShare.
  794. //
  795. // RETURNS:
  796. //
  797. // TRUE if the party can join the share.
  798. //
  799. // FALSE if the party can NOT join the share.
  800. //
  801. //
  802. ASPerson * ASShare::SC_PartyJoiningShare
  803. (
  804. UINT mcsID,
  805. LPSTR szName,
  806. UINT cbCaps,
  807. LPVOID pCaps
  808. )
  809. {
  810. BOOL rc = FALSE;
  811. ASPerson * pasPerson = NULL;
  812. // DebugEntry(ASShare::SC_PartyJoiningShare);
  813. //
  814. // Allocate an ASPerson for this dude. If this is the local person,
  815. // it gets stuck at the front. Otherwise it gets stuck just past
  816. // the front.
  817. //
  818. pasPerson = SC_PersonAllocate(mcsID, szName);
  819. if (!pasPerson)
  820. {
  821. ERROR_OUT(("SC_PartyAdded: can't allocate ASPerson for [%d]", mcsID));
  822. DC_QUIT;
  823. }
  824. //
  825. // DO THIS FIRST -- we need the person's caps set up
  826. //
  827. if (!CPC_PartyJoiningShare(pasPerson, cbCaps, pCaps))
  828. {
  829. ERROR_OUT(("CPC_PartyJoiningShare failed for [%d]", pasPerson->mcsID));
  830. DC_QUIT;
  831. }
  832. //
  833. // Call the component joined routines.
  834. //
  835. if (!DCS_PartyJoiningShare(pasPerson))
  836. {
  837. ERROR_OUT(("DCS_PartyJoiningShare failed for [%d]", pasPerson->mcsID));
  838. DC_QUIT;
  839. }
  840. if (!CM_PartyJoiningShare(pasPerson))
  841. {
  842. ERROR_OUT(("CM_PartyJoiningShare failed for [%d]", pasPerson->mcsID));
  843. DC_QUIT;
  844. }
  845. if (!HET_PartyJoiningShare(pasPerson))
  846. {
  847. ERROR_OUT(("HET_PartyJoiningShare failed for [%d]", pasPerson->mcsID));
  848. DC_QUIT;
  849. }
  850. //
  851. // NOW THE PERSON IS JOINED.
  852. // Recalculate capabilities of the share with this new person.
  853. //
  854. SC_RecalcCaps(TRUE);
  855. rc = TRUE;
  856. DC_EXIT_POINT:
  857. if (!rc)
  858. {
  859. //
  860. // Don't worry, this person object will still be cleaned up,
  861. // code at a higher layer will free by using the MCSID.
  862. //
  863. pasPerson = NULL;
  864. }
  865. // DebugExitPVOID(ASShare::SC_PartyJoiningShare, pasPerson);
  866. return(pasPerson);
  867. }
  868. //
  869. // SC_RecalcCaps()
  870. //
  871. // Recalculates share capabilities after somebody has joined or left the
  872. // share.
  873. //
  874. void ASShare::SC_RecalcCaps(BOOL fJoiner)
  875. {
  876. ASPerson * pasT;
  877. DebugEntry(ASShare::SC_RecalcCaps);
  878. //
  879. // DO THIS FIRST -- recalculate the share version.
  880. //
  881. ValidatePerson(m_pasLocal);
  882. m_scShareVersion = m_pasLocal->cpcCaps.general.version;
  883. for (pasT = m_pasLocal->pasNext; pasT != NULL; pasT = pasT->pasNext)
  884. {
  885. ValidatePerson(pasT);
  886. m_scShareVersion = min(m_scShareVersion, pasT->cpcCaps.general.version);
  887. }
  888. //
  889. // Do viewing & hosting stuff first
  890. //
  891. DCS_RecalcCaps(fJoiner);
  892. OE_RecalcCaps(fJoiner);
  893. //
  894. // Do hosting stuff second
  895. //
  896. USR_RecalcCaps(fJoiner);
  897. CM_RecalcCaps(fJoiner);
  898. PM_RecalcCaps(fJoiner);
  899. SBC_RecalcCaps(fJoiner);
  900. SSI_RecalcCaps(fJoiner);
  901. DebugExitVOID(ASShare::SC_RecalcCaps);
  902. }
  903. //
  904. // FUNCTION: SC_PartyLeftShare()
  905. //
  906. // DESCRIPTION:
  907. //
  908. // Called when a party has left the share.
  909. //
  910. //
  911. void ASShare::SC_PartyLeftShare(UINT_PTR mcsID)
  912. {
  913. ASPerson * pasPerson;
  914. ASPerson * pasT;
  915. DebugEntry(SC_PartyLeftShare);
  916. if (!SC_ValidateNetID(mcsID, &pasPerson))
  917. {
  918. TRACE_OUT(("Couldn't find ASPerson for [%d]", mcsID));
  919. DC_QUIT;
  920. }
  921. // Tell the UI this dude is gone
  922. if (!pasPerson->cpcCaps.share.gccID)
  923. {
  924. WARNING_OUT(("Skipping PartyLeftShare for person [%d], no GCC id",
  925. pasPerson->mcsID));
  926. DC_QUIT;
  927. }
  928. DCS_NotifyUI(SH_EVT_PERSON_LEFT, pasPerson->cpcCaps.share.gccID, 0);
  929. //
  930. // Tell everybody this person is gone.
  931. //
  932. //
  933. // Notes on order of PartyLeftShare calls
  934. //
  935. // 1. HET must be called first, since that halts any sharing from this
  936. // person, before we kick the person out of the share.
  937. //
  938. // 2. CA must be called before IM (as CA calls IM functions)
  939. //
  940. //
  941. // This will stop hosting early
  942. HET_PartyLeftShare(pasPerson);
  943. CA_PartyLeftShare(pasPerson);
  944. CM_PartyLeftShare(pasPerson);
  945. SWL_PartyLeftShare(pasPerson);
  946. VIEW_PartyLeftShare(pasPerson);
  947. PM_PartyLeftShare(pasPerson);
  948. RBC_PartyLeftShare(pasPerson);
  949. OD2_PartyLeftShare(pasPerson);
  950. OE_PartyLeftShare(pasPerson);
  951. DCS_PartyLeftShare(pasPerson);
  952. //
  953. // Free the person
  954. //
  955. SC_PersonFree(pasPerson);
  956. //
  957. // Recalculate the caps with him gone. But there's no point in doing
  958. // this if it's the local dude, since the share will exit imminently.
  959. //
  960. if (m_pasLocal)
  961. {
  962. SC_RecalcCaps(FALSE);
  963. }
  964. DC_EXIT_POINT:
  965. DebugExitVOID(ASShare::SC_PartyLeftShare);
  966. }
  967. //
  968. // FUNCTION: SCCheckForCMCall
  969. //
  970. // DESCRIPTION:
  971. //
  972. // This is called when we want to check if a CM call now exists (and do
  973. // whatever is appropriate to join it etc).
  974. //
  975. // PARAMETERS: NONE
  976. //
  977. // RETURNS: TRUE if success; otherwise, FALSE.
  978. //
  979. //
  980. void SCCheckForCMCall(void)
  981. {
  982. CM_STATUS cmStatus;
  983. DebugEntry(SCCheckForCMCall);
  984. ASSERT(g_asSession.scState == SCS_INIT);
  985. //
  986. // See if a call already exists
  987. //
  988. if (!g_asSession.callID)
  989. {
  990. if (CMS_GetStatus(&cmStatus))
  991. {
  992. //
  993. // The AS lock protects the g_asSession fields.
  994. //
  995. TRACE_OUT(("AS LOCK: SCCheckForCMCall"));
  996. UT_Lock(UTLOCK_AS);
  997. g_asSession.callID = cmStatus.callID;
  998. g_asSession.attendeePermissions = cmStatus.attendeePermissions;
  999. WARNING_OUT(("Local mtg settings 0x%08lx", g_asSession.attendeePermissions));
  1000. //
  1001. // This is the time to update our local person name. It's
  1002. // on our thread, but before the control packets exchange it
  1003. //
  1004. lstrcpy(g_asSession.achLocalName, cmStatus.localName);
  1005. g_asSession.cchLocalName = lstrlen(g_asSession.achLocalName);
  1006. TRACE_OUT(("Local Name is %s", g_asSession.achLocalName));
  1007. g_asSession.gccID = cmStatus.localHandle;
  1008. UT_Unlock(UTLOCK_AS);
  1009. TRACE_OUT(("AS UNLOCK: SCCheckForCMCall"));
  1010. }
  1011. }
  1012. if (g_asSession.callID)
  1013. {
  1014. SC_CreateShare(S20_JOIN);
  1015. }
  1016. DebugExitVOID(SCCheckForCMCall);
  1017. }
  1018. #ifdef _DEBUG
  1019. void ASShare::ValidatePerson(ASPerson * pasPerson)
  1020. {
  1021. ASSERT(!IsBadWritePtr(pasPerson, sizeof(ASPerson)));
  1022. ASSERT(!lstrcmp(pasPerson->stamp.idStamp, "ASPerso"));
  1023. ASSERT(pasPerson->mcsID != MCSID_NULL);
  1024. }
  1025. void ASShare::ValidateView(ASPerson * pasPerson)
  1026. {
  1027. ValidatePerson(pasPerson);
  1028. ASSERT(!IsBadWritePtr(pasPerson->m_pView, sizeof(ASView)));
  1029. ASSERT(!lstrcmp(pasPerson->m_pView->stamp.idStamp, "ASVIEW"));
  1030. }
  1031. #endif // _DEBUG
  1032. //
  1033. // SC_PersonAllocate()
  1034. // This allocates a new ASPerson structure, fills in the debug/mcsID fields,
  1035. // and links it into the people-in-the-conference list.
  1036. //
  1037. // Eventually, all the PartyJoiningShare routines that simply init a field
  1038. // should go away and that info put here.
  1039. //
  1040. ASPerson * ASShare::SC_PersonAllocate(UINT mcsID, LPSTR szName)
  1041. {
  1042. ASPerson * pasNew;
  1043. // DebugEntry(ASShare::SC_PersonAllocate);
  1044. pasNew = new ASPerson;
  1045. if (!pasNew)
  1046. {
  1047. ERROR_OUT(("Unable to allocate a new ASPerson"));
  1048. DC_QUIT;
  1049. }
  1050. ZeroMemory(pasNew, sizeof(*pasNew));
  1051. SET_STAMP(pasNew, Person);
  1052. //
  1053. // Set up mcsID and name
  1054. //
  1055. pasNew->mcsID = mcsID;
  1056. lstrcpyn(pasNew->scName, szName, TSHR_MAX_PERSON_NAME_LEN);
  1057. UT_Lock(UTLOCK_AS);
  1058. //
  1059. // Is this the local person?
  1060. //
  1061. if (!m_pasLocal)
  1062. {
  1063. m_pasLocal = pasNew;
  1064. }
  1065. else
  1066. {
  1067. UINT streamID;
  1068. //
  1069. // This is a remote. Set up the sync status right away in case
  1070. // the join process fails in the middle. Cleaning up will undo
  1071. // this always.
  1072. //
  1073. //
  1074. // Mark this person's streams as needing a sync from us before we
  1075. // can send data to him
  1076. // And and that we need a sync from him on each stream before we'll
  1077. // receive data from him
  1078. //
  1079. for (streamID = SC_STREAM_LOW; streamID <= SC_STREAM_HIGH; streamID++ )
  1080. {
  1081. //
  1082. // Set up the sync status.
  1083. //
  1084. ASSERT(pasNew->scSyncSendStatus[streamID-1] == SC_NOT_SYNCED);
  1085. ASSERT(pasNew->scSyncRecStatus[streamID-1] == SC_NOT_SYNCED);
  1086. m_ascSynced[streamID-1]++;
  1087. }
  1088. //
  1089. // Link into list
  1090. //
  1091. pasNew->pasNext = m_pasLocal->pasNext;
  1092. m_pasLocal->pasNext = pasNew;
  1093. }
  1094. UT_Unlock(UTLOCK_AS);
  1095. DC_EXIT_POINT:
  1096. // DebugExitPVOID(ASShare::SC_PersonAllocate, pasNew);
  1097. return(pasNew);
  1098. }
  1099. //
  1100. // SC_PersonFree()
  1101. // This gets a person out of the linked list and frees the memory for them.
  1102. //
  1103. void ASShare::SC_PersonFree(ASPerson * pasFree)
  1104. {
  1105. ASPerson ** ppasPerson;
  1106. UINT streamID;
  1107. DebugEntry(ASShare::SC_PersonFree);
  1108. ValidatePerson(pasFree);
  1109. for (ppasPerson = &m_pasLocal; *(ppasPerson) != NULL; ppasPerson = &((*ppasPerson)->pasNext))
  1110. {
  1111. if ((*ppasPerson) == pasFree)
  1112. {
  1113. //
  1114. // Found it.
  1115. //
  1116. TRACE_OUT(("SC_PersonUnhook: unhooking person [%d]", pasFree->mcsID));
  1117. if (pasFree == m_pasLocal)
  1118. {
  1119. ASSERT(pasFree->pasNext == NULL);
  1120. }
  1121. else
  1122. {
  1123. //
  1124. // Clear syncs
  1125. //
  1126. // If this person was never synced, subtract them from the
  1127. // global "needing sync" count on each stream
  1128. //
  1129. for (streamID = SC_STREAM_LOW; streamID <= SC_STREAM_HIGH; streamID++ )
  1130. {
  1131. if (pasFree->scSyncSendStatus[streamID-1] == SC_NOT_SYNCED)
  1132. {
  1133. ASSERT(m_ascSynced[streamID-1] > 0);
  1134. m_ascSynced[streamID-1]--;
  1135. }
  1136. }
  1137. }
  1138. UT_Lock(UTLOCK_AS);
  1139. //
  1140. // Fix up linked list.
  1141. //
  1142. (*ppasPerson) = pasFree->pasNext;
  1143. #ifdef _DEBUG
  1144. ZeroMemory(pasFree, sizeof(ASPerson));
  1145. #endif // _DEBUG
  1146. delete pasFree;
  1147. UT_Unlock(UTLOCK_AS);
  1148. DC_QUIT;
  1149. }
  1150. }
  1151. //
  1152. // We didn't find this guy in the list--this is very bad.
  1153. //
  1154. ERROR_OUT(("SC_PersonFree: didn't find person %d", pasFree));
  1155. DC_EXIT_POINT:
  1156. DebugExitVOID(ASShare::SC_PersonFree);
  1157. }
  1158. //
  1159. // SC_AllocPkt()
  1160. // Allocates a SEND packet
  1161. //
  1162. PS20DATAPACKET ASShare::SC_AllocPkt
  1163. (
  1164. UINT streamID,
  1165. UINT_PTR nodeID,
  1166. UINT_PTR cbSizePkt
  1167. )
  1168. {
  1169. PS20DATAPACKET pPacket = NULL;
  1170. // DebugEntry(ASShare::SC_AllocPkt);
  1171. if (g_asSession.scState != SCS_SHARING)
  1172. {
  1173. TRACE_OUT(("SC_AllocPkt failed; share is ending"));
  1174. DC_QUIT;
  1175. }
  1176. ASSERT((streamID >= SC_STREAM_LOW) && (streamID <= SC_STREAM_HIGH));
  1177. ASSERT(cbSizePkt >= sizeof(S20DATAPACKET));
  1178. //
  1179. // We'd better not be in the middle of a sync!
  1180. //
  1181. ASSERT(!m_scfInSync);
  1182. //
  1183. // Try and send any outstanding syncs
  1184. //
  1185. if (!SCSyncStream(streamID))
  1186. {
  1187. //
  1188. // If the stream is still not synced, don't allocate the packet
  1189. //
  1190. WARNING_OUT(("SC_AllocPkt failed; outstanding syncs are present"));
  1191. DC_QUIT;
  1192. }
  1193. pPacket = S20_AllocDataPkt(streamID, nodeID, cbSizePkt);
  1194. DC_EXIT_POINT:
  1195. // DebugExitPVOID(ASShare::SC_AllocPkt, pPacket);
  1196. return(pPacket);
  1197. }
  1198. //
  1199. // SCSyncStream()
  1200. //
  1201. // This broadcasts a SNI sync packet intended for a new person who has just
  1202. // joined the share. That person ignores all received data from us until
  1203. // they get the sync. That's because data in transit before they are synced
  1204. // could refer to PKZIP data they don't have, second level order encoding
  1205. // info they can't decode, orders they can't process, etc.
  1206. //
  1207. // When we receive a SYNC from a remote, we then know that following
  1208. // data from that remote will make sense. The remote has settled us into
  1209. // the share, and the data encorporates our capabilities and will not refer
  1210. // to prior state info from before we joined the share.
  1211. //
  1212. // NOTE that in 2.x, this was O(N^2) where N is the number of people now in
  1213. // the share! Each person in the share would send SNI sync packets for each
  1214. // stream for each other person in the share, even for people who weren't new.
  1215. // But those people wouldn't reset received state, and would (could) continue
  1216. // processing data from us. When they finally got their sync packet, it
  1217. // would do nothing! Even worst, 2 of the 5 streams are never used,
  1218. // and one stream is only used when a person is hosting. So 3 of these 5
  1219. // O(N^2) broadcasts were useless all or the majority of the time.
  1220. //
  1221. // So now we only send SNI sync packets for new joiners. This makes joining
  1222. // an O(N) broadcast algorithm.
  1223. //
  1224. // LAURABU BOGUS
  1225. // Post Beta1, we can make this even better. Each broadcast is itself O(N)
  1226. // packets. So for beta1, joining/syncing is O(N^2) packets instead of
  1227. // O(N^3) packets. With targeted sends (not broadcasts) whenever possible,
  1228. // we can drop this to O(N) total packets.
  1229. //
  1230. // NOTE also that no real app sharing packets are sent on a stream until
  1231. // we have fully synced everybody. That means we've reduced a lot the
  1232. // lag between somebody dialing into a conference and their seeing a result,
  1233. // and everybody else being able to work again.
  1234. //
  1235. BOOL ASShare::SCSyncStream(UINT streamID)
  1236. {
  1237. ASPerson * pasPerson;
  1238. PSNIPACKET pSNIPacket;
  1239. BOOL rc = TRUE;
  1240. DebugEntry(ASShare::SCSyncStream);
  1241. //
  1242. // Loop through each person in the call broadcasting sync packets as
  1243. // necessary.
  1244. //
  1245. // LAURABU BOGUS
  1246. // We can change this to a targeted send after Beta 1.
  1247. //
  1248. //
  1249. // Note that new people are added to the front of the this. So we will
  1250. // bail out of this loop very quickly when sending syncs to newcomers.
  1251. //
  1252. ValidatePerson(m_pasLocal);
  1253. pasPerson = m_pasLocal->pasNext;
  1254. while ((m_ascSynced[streamID-1] > 0) && (pasPerson != NULL))
  1255. {
  1256. ValidatePerson(pasPerson);
  1257. //
  1258. // If this person is new, we need to send them a SYNC packet so
  1259. // they know we are done processing their join and know they
  1260. // are in the share.
  1261. //
  1262. if (pasPerson->scSyncSendStatus[streamID-1] != SC_SYNCED)
  1263. {
  1264. TRACE_OUT(("Syncing stream %d for person [%d] in broadcast way",
  1265. streamID, pasPerson->mcsID));
  1266. //
  1267. // YES, syncs are broadcast even though they have the mcsID of
  1268. // just one person, the person they are intended for. Since we
  1269. // broadcast all state data, we also broadcast syncs. That's
  1270. // the only way to make sure the packets arrive in the same
  1271. // order, SYNC before data.
  1272. //
  1273. pSNIPacket = (PSNIPACKET)S20_AllocDataPkt(streamID,
  1274. g_s20BroadcastID, sizeof(SNIPACKET));
  1275. if (!pSNIPacket)
  1276. {
  1277. TRACE_OUT(("Failed to alloc SNI sync packet"));
  1278. rc = FALSE;
  1279. break;
  1280. }
  1281. //
  1282. // Set SNI packet fields
  1283. //
  1284. pSNIPacket->header.data.dataType = DT_SNI;
  1285. pSNIPacket->message = SNI_MSG_SYNC;
  1286. pSNIPacket->destination = (NET_UID)pasPerson->mcsID;
  1287. S20_SendDataPkt(streamID, g_s20BroadcastID, &(pSNIPacket->header));
  1288. pasPerson->scSyncSendStatus[streamID-1] = SC_SYNCED;
  1289. ASSERT(m_ascSynced[streamID-1] > 0);
  1290. m_ascSynced[streamID-1]--;
  1291. }
  1292. pasPerson = pasPerson->pasNext;
  1293. }
  1294. DebugExitBOOL(ASShare::SCSyncStream, rc);
  1295. return(rc);
  1296. }
  1297.