Source code of Windows XP (NT5)
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.

2188 lines
64 KiB

  1. /****************************************************************************/
  2. /* */
  3. /* ERNCCONF.CPP */
  4. /* */
  5. /* Base Conference class for the Reference System Node Controller. */
  6. /* */
  7. /* Copyright Data Connection Ltd. 1995 */
  8. /* */
  9. /****************************************************************************/
  10. /* Changes: */
  11. /* */
  12. /* 12Jul95 NFC Created. */
  13. /* 05Oct95 NFC SFR 6206 Treat a "Join" as an incoming call. */
  14. /* 11Oct95 PM Relax checks on conference termination to */
  15. /* prevent "no win" situations */
  16. /* Support START_ALTERNATE from TPhys API */
  17. /* */
  18. /****************************************************************************/
  19. #include "precomp.h"
  20. DEBUG_FILEZONE(ZONE_GCC_NC);
  21. #include "ernccons.h"
  22. #include "nccglbl.hpp"
  23. #include "erncvrsn.hpp"
  24. #include <cuserdta.hpp>
  25. #include "connect.h"
  26. #include "erncconf.hpp"
  27. #include "ernctrc.h"
  28. #include "ernccm.hpp"
  29. #include <iappldr.h>
  30. #include "plgxprt.h"
  31. #include "nmremote.h"
  32. extern PController g_pMCSController;
  33. DCRNCConference::
  34. DCRNCConference
  35. (
  36. LPCWSTR pwcszConfName,
  37. GCCConfID nConfID,
  38. BOOL fSecure,
  39. HRESULT *pRetCode
  40. )
  41. :
  42. CRefCount(MAKE_STAMP_ID('N','C','C','F')),
  43. m_fNotifyToDo(FALSE),
  44. m_fActive(TRUE),
  45. #ifdef _DEBUG
  46. m_fAppendedToConfList(FALSE),
  47. #endif
  48. m_pInviteUI(NULL),
  49. m_pszFirstRemoteNodeAddress(NULL),
  50. m_nConfID(nConfID),
  51. m_eState(CONF_ST_UNINITIALIZED),
  52. m_fIncoming(FALSE),
  53. m_pbHashedPassword(NULL),
  54. m_cbHashedPassword(0),
  55. m_pwszPassword(NULL),
  56. m_pszNumericPassword(NULL),
  57. // T120 conference
  58. m_eT120State(T120C_ST_IDLE),
  59. m_nidMyself(0),
  60. m_fSecure(fSecure),
  61. m_nInvalidPasswords(0)
  62. {
  63. DebugEntry(DCRNCConference::DCRNCConference);
  64. // Save the conference name.
  65. DBG_SAVE_FILE_LINE
  66. m_pwszConfName = ::My_strdupW(pwcszConfName);
  67. if (! ::IsEmptyStringW(m_pwszConfName))
  68. {
  69. *pRetCode = NO_ERROR;
  70. }
  71. else
  72. {
  73. *pRetCode = (NULL == m_pwszConfName) ? UI_RC_OUT_OF_MEMORY :
  74. UI_RC_NO_CONFERENCE_NAME;
  75. }
  76. // T120 conference
  77. m_ConfName.numeric_string = NULL;
  78. m_ConfName.text_string = NULL;
  79. DebugExitVOID(DCRNCConference::DCRNCConference);
  80. }
  81. /****************************************************************************/
  82. /* Destructor - see erncconf.h */
  83. /****************************************************************************/
  84. DCRNCConference::
  85. ~DCRNCConference(void)
  86. {
  87. DebugEntry(DCRNCConference::~DCRNCConference);
  88. ASSERT(! m_fAppendedToConfList);
  89. // delete all the name strings
  90. LPSTR pszStr;
  91. while (NULL != (pszStr = m_NodeIdNameList.Get()))
  92. {
  93. delete [] pszStr;
  94. }
  95. // Delete all the usr data
  96. CNCUserDataList *pUserDataList;
  97. while (NULL != (pUserDataList = m_UserDataList.Get()))
  98. {
  99. delete pUserDataList;
  100. }
  101. delete m_pwszConfName;
  102. // If there is a password, delete it.
  103. delete []m_pbHashedPassword;
  104. delete m_pwszPassword;
  105. delete m_pszNumericPassword;
  106. delete m_pszFirstRemoteNodeAddress;
  107. // T120 conference
  108. delete m_ConfName.numeric_string;
  109. DebugExitVOID(DCRNCConference::~DCRNCConference);
  110. }
  111. void DCRNCConference::
  112. OnRemoved(BOOL fReleaseNow)
  113. {
  114. DebugEntry(DCRNCConference::OnRemoved);
  115. CLogicalConnection *pConEntry;
  116. #ifdef _DEBUG
  117. m_fAppendedToConfList = FALSE;
  118. #endif
  119. // Issue a request to leave the conference.
  120. // This request may fail, but may as well let leave validate
  121. // itself, rather than put an extra check in here.
  122. // See comments in RemoveConference() and Leave() for more details
  123. // if interested.
  124. if (T120C_ST_PENDING_DISCONNECT != m_eT120State &&
  125. T120C_ST_PENDING_TERMINATE != m_eT120State)
  126. {
  127. Leave();
  128. }
  129. // Take the conference out of the list of pending invites.
  130. g_pNCConfMgr->RemoveInviteIndWorkItem(m_pInviteUI);
  131. // End all physical connections in use by this conference,
  132. // and inform the user of the results of pending events.
  133. while (NULL != (pConEntry = m_ConnList.Get()))
  134. {
  135. pConEntry->Delete(UI_RC_CONFERENCE_GOING_DOWN);
  136. }
  137. //
  138. // LONCHANC: This destructor may be called inside
  139. // ConfMgr::ReleaseInterface(). As a result, the global pointer
  140. // to the callback interface may already be nulled out.
  141. // Check it before use it.
  142. //
  143. // ASSERT(2 == GetRefCount());
  144. // Tell UI its handle to conference is no longer valid.
  145. if (NULL != g_pCallbackInterface)
  146. {
  147. g_pCallbackInterface->OnConferenceEnded((CONF_HANDLE) this);
  148. }
  149. else
  150. {
  151. ERROR_OUT(("DCRNCConference::OnRemoved: g_pCallbackInterface is null"));
  152. }
  153. // ASSERT(1 == GetRefCount());
  154. if (fReleaseNow)
  155. {
  156. ReleaseNow();
  157. }
  158. else
  159. {
  160. Release();
  161. }
  162. DebugExitVOID(DCRNCConference::OnRemoved);
  163. }
  164. //
  165. // IDataConference Interface
  166. //
  167. STDMETHODIMP_(void) DCRNCConference::
  168. ReleaseInterface(void)
  169. {
  170. DebugEntry(DCRNCConference::ReleaseInterface);
  171. InterfaceEntry();
  172. Release();
  173. DebugExitVOID(DCRNCConference::ReleaseInterface);
  174. }
  175. STDMETHODIMP_(UINT_PTR) DCRNCConference::
  176. GetConferenceID(void)
  177. {
  178. DebugEntry(DCRNCConference::GetConferenceID);
  179. InterfaceEntry();
  180. DebugExitINT(DCRNCConference::GetConferenceID, (UINT) m_nConfID);
  181. return m_nConfID;
  182. }
  183. STDMETHODIMP DCRNCConference::
  184. Leave(void)
  185. {
  186. DebugEntry(DCRNCConference::Leave);
  187. InterfaceEntry();
  188. GCCError GCCrc;
  189. HRESULT hr;
  190. switch (m_eT120State)
  191. {
  192. // LONCHANC: Added the following two cases for cancellation.
  193. case T120C_ST_PENDING_START_CONFIRM:
  194. case T120C_ST_PENDING_JOIN_CONFIRM:
  195. case T120C_ST_PENDING_ROSTER_ENTRY:
  196. case T120C_ST_PENDING_ROSTER_MESSAGE:
  197. case T120C_ST_PENDING_ANNOUNCE_PERMISSION:
  198. // User has called leave on a conference when it is being brought up.
  199. // Drop through to issue a disconnect request to T120.
  200. case T120C_ST_CONF_STARTED:
  201. // Set the state of the conference to note that we are
  202. // disconnecting from T120.
  203. // LONCHANC: this is a must to avoid reentrance of this Leave()
  204. // when direct InviteConfirm hits Node Controller later.
  205. m_eT120State = T120C_ST_PENDING_DISCONNECT;
  206. // User has requested to leave the conference after it has been
  207. // started as a T120 conference, so ask T120 to end the conference
  208. // before removing internal data structures.
  209. GCCrc = g_pIT120ControlSap->ConfDisconnectRequest(m_nConfID);
  210. hr = ::GetGCCRCDetails(GCCrc);
  211. TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfDisconnectRequest, rc=%d", GCCrc));
  212. if (NO_ERROR == hr)
  213. {
  214. break;
  215. }
  216. // T120 won't let us leave a conference that we think we are in.
  217. // Take this to mean that T120 doesn't know about the conference
  218. // anymore and just destroy our own knowledge of the conference.
  219. WARNING_OUT(("DCRNCConference::Leave: Failed to leave conference, GCC error %d", GCCrc));
  220. // Drop through to destroy our references.
  221. case T120C_ST_IDLE:
  222. // User has requested to leave a conference that has not been
  223. // started.
  224. // This should only happen when told that a conference join
  225. // request supplied an invalid password and the user gives up
  226. // on attempting to join the conference (or shuts down conferencing).
  227. // Just do the same processing as would be done when a T120
  228. // disconnect confirmation fires.
  229. g_pNCConfMgr->RemoveConference(this);
  230. hr = NO_ERROR;
  231. break;
  232. case T120C_ST_PENDING_DISCONNECT:
  233. case T120C_ST_PENDING_TERMINATE:
  234. // User has requested to leave a conference that is already
  235. // going down (most likely because of a prior request to leave).
  236. hr = UI_RC_CONFERENCE_GOING_DOWN;
  237. WARNING_OUT(("DCRNCConference::Leave: conference already going down, state=%d", m_eT120State));
  238. break;
  239. default:
  240. // User has called leave on a conference when he shouldn't
  241. // (e.g. when it is being brought up).
  242. // This is very unlikely to happen as the user doesn't know
  243. // the conference handle at this point.
  244. hr = UI_RC_INVALID_REQUEST;
  245. ERROR_OUT(("DCRNCConference::Leave: invalid state=%d", m_eT120State));
  246. break;
  247. }
  248. DebugExitHRESULT(DCRNCConference::Leave, hr);
  249. return hr;
  250. }
  251. STDMETHODIMP DCRNCConference::
  252. EjectUser ( UINT nidEjected )
  253. {
  254. DebugEntry(DCRNCConference::EjectUser);
  255. InterfaceEntry();
  256. GCCError GCCrc = g_pIT120ControlSap->ConfEjectUserRequest(m_nConfID, (UserID) nidEjected, GCC_REASON_USER_INITIATED);
  257. HRESULT hr = ::GetGCCRCDetails(GCCrc);
  258. if (NO_ERROR != hr)
  259. {
  260. ERROR_OUT(("DCRNCConference::EjectUser: Failed to eject user conference, GCC error %d", GCCrc));
  261. }
  262. CLogicalConnection *pConEntry = GetConEntryByNodeID((GCCNodeID) nidEjected);
  263. if (NULL != pConEntry)
  264. {
  265. pConEntry->Delete(UI_RC_USER_DISCONNECTED);
  266. }
  267. DebugExitHRESULT(DCRNCConference::EjectUser, hr);
  268. return hr;
  269. }
  270. STDMETHODIMP DCRNCConference::
  271. Invite
  272. (
  273. LPCSTR pcszNodeAddress,
  274. USERDATAINFO aInfo[],
  275. UINT cInfo,
  276. REQUEST_HANDLE * phRequest
  277. )
  278. {
  279. DebugEntry(DCRNCConference::Invite);
  280. InterfaceEntry();
  281. HRESULT hr;
  282. #if defined(TEST_PLUGGABLE) && defined(_DEBUG)
  283. if (g_fWinsockDisabled)
  284. {
  285. pcszNodeAddress = ::FakeNodeAddress(pcszNodeAddress);
  286. }
  287. #endif
  288. if (NULL != pcszNodeAddress && NULL != phRequest)
  289. {
  290. // if winsock is disabled, block any IP address or machine name
  291. if (g_fWinsockDisabled)
  292. {
  293. if (! IsValidPluggableTransportName(pcszNodeAddress))
  294. {
  295. return UI_RC_NO_WINSOCK;
  296. }
  297. }
  298. // Check that person is not already in the conference.
  299. if (GetConEntry((LPSTR) pcszNodeAddress))
  300. {
  301. hr = UI_RC_ALREADY_IN_CONFERENCE;
  302. }
  303. else
  304. {
  305. hr = StartConnection((LPSTR) pcszNodeAddress,
  306. CONF_CON_PENDING_INVITE,
  307. aInfo,
  308. cInfo,
  309. m_fSecure,
  310. phRequest);
  311. }
  312. if (NO_ERROR != hr)
  313. {
  314. ERROR_OUT(("Error adding connection"));
  315. }
  316. }
  317. else
  318. {
  319. hr = (pcszNodeAddress == NULL) ? UI_RC_NO_ADDRESS : UI_RC_BAD_PARAMETER;
  320. ERROR_OUT(("DCRNCConference::Invite: invalid parameters, hr=0x%x", (UINT) hr));
  321. }
  322. // Sit and wait for the connection to complete before continuing.
  323. DebugExitHRESULT(DCRNCConference::Invite, hr);
  324. return hr;
  325. }
  326. STDMETHODIMP DCRNCConference::
  327. CancelInvite ( REQUEST_HANDLE hRequest )
  328. {
  329. DebugEntry(DCRNCConference::CancelInvite);
  330. InterfaceEntry();
  331. HRESULT hr;
  332. CLogicalConnection *pConEntry = (CLogicalConnection *) hRequest;
  333. if (NULL != pConEntry)
  334. {
  335. ConnectionHandle hConn = pConEntry->GetInviteReqConnHandle();
  336. ASSERT(NULL != hConn);
  337. g_pIT120ControlSap->CancelInviteRequest(m_nConfID, hConn);
  338. hr = NO_ERROR;
  339. }
  340. else
  341. {
  342. hr = UI_RC_BAD_PARAMETER;
  343. }
  344. DebugExitHRESULT(DCRNCConference::CancelInvite, hr);
  345. return hr;
  346. }
  347. STDMETHODIMP DCRNCConference::
  348. GetCred ( PBYTE *ppbCred, DWORD *pcbCred )
  349. {
  350. DebugEntry(DCRNCConference::GetCred);
  351. HRESULT hr = UI_RC_INTERNAL_ERROR;
  352. if (m_pbCred)
  353. {
  354. *ppbCred = m_pbCred;
  355. *pcbCred = m_cbCred;
  356. hr = NO_ERROR;
  357. }
  358. DebugExitHRESULT(DCRNCConference::GetCred, hr);
  359. return hr;
  360. }
  361. STDMETHODIMP DCRNCConference::
  362. InviteResponse ( BOOL fResponse )
  363. {
  364. DebugEntry(DCRNCConference::InviteResponse);
  365. InterfaceEntry();
  366. HRESULT hrResponse = fResponse ? NO_ERROR : UI_RC_USER_REJECTED;
  367. HRESULT hr = InviteResponse(hrResponse);
  368. DebugExitHRESULT(DCRNCConferenceManager::InviteResponse, hr);
  369. return hr;
  370. }
  371. HRESULT DCRNCConference::
  372. InviteResponse ( HRESULT hrResponse )
  373. {
  374. DebugEntry(DCRNCConference::InviteResponse);
  375. InterfaceEntry();
  376. GCCResult Result = ::MapRCToGCCResult(hrResponse);
  377. GCCError GCCrc = g_pIT120ControlSap->ConfInviteResponse(
  378. m_nConfID,
  379. NULL,
  380. m_fSecure,
  381. NULL, // domain parms
  382. 0, // number_of_network_addresses
  383. NULL, // local_network_address_list
  384. g_nVersionRecords, // number_of_user_data_members
  385. g_ppVersionUserData, // user_data_list
  386. Result);
  387. if ((GCCrc == GCC_RESULT_SUCCESSFUL) && (Result == GCC_RESULT_SUCCESSFUL))
  388. {
  389. // Have successfully posted an invite response acceptance.
  390. // Note that the conference is expecting permission to
  391. // announce its presence.
  392. m_eT120State = T120C_ST_PENDING_ANNOUNCE_PERMISSION;
  393. }
  394. else
  395. {
  396. // Have rejected/failed a request to be invited into a conference.
  397. // Remove the references that were created to track the potential
  398. // new conference.
  399. g_pNCConfMgr->RemoveConference(this);
  400. }
  401. HRESULT hr = ::GetGCCRCDetails(GCCrc);
  402. DebugExitHRESULT(DCRNCConferenceManager::InviteResponse, hr);
  403. return hr;
  404. }
  405. STDMETHODIMP DCRNCConference::
  406. JoinResponse ( BOOL fResponse )
  407. {
  408. DebugEntry(DCRNCConference::JoinResponse);
  409. InterfaceEntry();
  410. HRESULT hr;
  411. CJoinIndWork *pJoinUI = g_pNCConfMgr->PeekFirstJoinIndWorkItem();
  412. if (NULL != pJoinUI)
  413. {
  414. if (pJoinUI->GetConference() == this)
  415. {
  416. if (fResponse && pJoinUI->GetConEntry()->NewLocalAddress())
  417. {
  418. AnnouncePresence();
  419. }
  420. hr = pJoinUI->Respond(fResponse ? GCC_RESULT_SUCCESSFUL : GCC_RESULT_USER_REJECTED);
  421. // Done responding to event, so can now remove from list and process
  422. // another pending event.
  423. // Note: since the handling of the previous event is still
  424. // potentially on the stack, this can cause the stack to grow,
  425. // but this should not be a problem for Win32.
  426. g_pNCConfMgr->RemoveJoinIndWorkItem(pJoinUI);
  427. }
  428. else
  429. {
  430. hr = UI_RC_BAD_PARAMETER;
  431. }
  432. }
  433. else
  434. {
  435. ERROR_OUT(("DCRNCConference::JoinResponse: Empty m_JoinIndWorkList, fResponse=%u", fResponse));
  436. hr = UI_RC_INTERNAL_ERROR;
  437. }
  438. DebugExitHRESULT(DCRNCConference::JoinResponse, hr);
  439. return hr;
  440. }
  441. STDMETHODIMP DCRNCConference::
  442. LaunchGuid
  443. (
  444. const GUID *pcGUID,
  445. UINT auNodeIDs[],
  446. UINT cNodes
  447. )
  448. {
  449. DebugEntry(DCRNCConference::LaunchGuid);
  450. InterfaceEntry();
  451. HRESULT hr;
  452. if (NULL != pcGUID)
  453. {
  454. //
  455. // We probably should support conference-wide app invoke by
  456. // cNodes==0 and auNodeIDs==NULL.
  457. // Implement it later...
  458. //
  459. if ((0 != cNodes) || (NULL != auNodeIDs))
  460. {
  461. // UserID is a short. We have to translate these UserID to a new array.
  462. // Try not to allocate memory for small array.
  463. UserID *pNodeIDs;
  464. const UINT c_cRemote = 16;
  465. UserID auidRemote[c_cRemote];
  466. if (cNodes <= c_cRemote)
  467. {
  468. pNodeIDs = auidRemote;
  469. }
  470. else
  471. {
  472. pNodeIDs = new UserID[cNodes];
  473. if (NULL == pNodeIDs)
  474. {
  475. hr = UI_RC_OUT_OF_MEMORY;
  476. goto MyExit;
  477. }
  478. }
  479. // Copy all the node IDs.
  480. for (UINT i = 0; i < cNodes; i++)
  481. {
  482. pNodeIDs[i] = (UserID)auNodeIDs[i];
  483. }
  484. // Construct the key
  485. GCCError GCCrc;
  486. GCCObjectKey * pAppKey;
  487. GCCAppProtocolEntity AppEntity;
  488. GCCAppProtocolEntity * pAppEntity;
  489. BYTE h221Key[CB_H221_GUIDKEY];
  490. ::CreateH221AppKeyFromGuid(h221Key, (GUID *) pcGUID);
  491. ::ZeroMemory(&AppEntity, sizeof(AppEntity));
  492. pAppKey = &AppEntity.session_key.application_protocol_key;
  493. pAppKey->key_type = GCC_H221_NONSTANDARD_KEY;
  494. pAppKey->h221_non_standard_id.length = sizeof(h221Key);
  495. pAppKey->h221_non_standard_id.value = h221Key;
  496. // AppEntity.session_key.session_id = 0; // default session
  497. // AppEntity.number_of_expected_capabilities = 0; // no capabilities
  498. // AppEntity.expected_capabilities_list = NULL;
  499. AppEntity.startup_channel_type = MCS_NO_CHANNEL_TYPE_SPECIFIED;
  500. AppEntity.must_be_invoked = TRUE;
  501. pAppEntity = &AppEntity;
  502. GCCrc = g_pIT120ControlSap->AppletInvokeRequest(m_nConfID, 1, &pAppEntity, cNodes, pNodeIDs);
  503. hr = ::GetGCCRCDetails(GCCrc);
  504. if (NO_ERROR != hr)
  505. {
  506. ERROR_OUT(("DCRNCConference::LaunchGuid: AppletInvokeRequest failed, GCCrc=%u", GCCrc));
  507. }
  508. if (pNodeIDs != auidRemote)
  509. {
  510. delete [] pNodeIDs;
  511. }
  512. }
  513. else
  514. {
  515. hr = UI_RC_BAD_PARAMETER;
  516. ERROR_OUT(("DCRNCConference::LaunchGuid: invalid combination, cNodes=%u. auNodeIDs=0x%p", cNodes, auNodeIDs));
  517. }
  518. }
  519. else
  520. {
  521. hr = UI_RC_BAD_PARAMETER;
  522. ERROR_OUT(("DCRNCConference::LaunchGuid: null pcGUID"));
  523. }
  524. MyExit:
  525. DebugExitHRESULT(DCRNCConference::LaunchGuid, hr);
  526. return hr;
  527. }
  528. STDMETHODIMP DCRNCConference::
  529. SetUserData
  530. (
  531. const GUID *pcGUID,
  532. UINT cbData,
  533. LPVOID pData
  534. )
  535. {
  536. DebugEntry(DCRNCConference::SetUserData);
  537. InterfaceEntry();
  538. HRESULT hr;
  539. if (0 != cbData || NULL != pData)
  540. {
  541. hr = m_LocalUserData.AddUserData((GUID *) pcGUID, cbData, pData);
  542. }
  543. else
  544. {
  545. hr = UI_RC_BAD_PARAMETER;
  546. ERROR_OUT(("DCRNCConference::SetUserData: invalid combination, cbData=%u. pData=0x%p", cbData, pData));
  547. }
  548. DebugExitHRESULT(DCRNCConference::SetUserData, hr);
  549. return hr;
  550. }
  551. STDMETHODIMP_(BOOL) DCRNCConference::
  552. IsSecure ()
  553. {
  554. return m_fSecure;
  555. }
  556. STDMETHODIMP DCRNCConference::
  557. SetSecurity ( BOOL fSecure )
  558. {
  559. m_fSecure = fSecure;
  560. return S_OK;
  561. }
  562. STDMETHODIMP DCRNCConference::
  563. UpdateUserData(void)
  564. {
  565. DebugEntry(DCRNCConference::UpdateUserData);
  566. InterfaceEntry();
  567. HRESULT hr = AnnouncePresence();
  568. DebugExitHRESULT(DCRNCConference::UpdateUserData, hr);
  569. return hr;
  570. }
  571. STDMETHODIMP DCRNCConference::
  572. GetLocalAddressList
  573. (
  574. LPWSTR pwszBuffer,
  575. UINT cchBuffer
  576. )
  577. {
  578. DebugEntry(DCRNCConference::GetLocalAddressList);
  579. InterfaceEntry();
  580. HRESULT hr;
  581. UINT cAddrs;
  582. LPCSTR *pAddresses = NULL;
  583. ASSERT(cchBuffer > 1); // buffer should have enough room for a double NULL terminator
  584. hr = m_LocalAddressList.GetLocalAddressList(&cAddrs, &pAddresses);
  585. if (NO_ERROR == hr)
  586. {
  587. LPWSTR pwszPos = pwszBuffer;
  588. for (UINT i = 0; i < cAddrs; i++)
  589. {
  590. ASSERT(pAddresses[i]);
  591. LPWSTR pwszAddress = ::AnsiToUnicode(pAddresses[i]);
  592. UINT cchAddress = ::My_strlenW(pwszAddress);
  593. if ((cchBuffer - (pwszPos - pwszBuffer)) <
  594. (RNC_GCC_TRANSPORT_AND_SEPARATOR_LENGTH + cchAddress + 2))
  595. {
  596. // NOTE: +2 insures room for the two '\0' chars
  597. // If there isn't room, break out here:
  598. break;
  599. }
  600. LStrCpyW(pwszPos, RNC_GCC_TRANSPORT_AND_SEPARATOR_UNICODE);
  601. pwszPos += RNC_GCC_TRANSPORT_AND_SEPARATOR_LENGTH;
  602. LStrCpyW(pwszPos, pwszAddress);
  603. pwszPos += cchAddress;
  604. *pwszPos = L'\0';
  605. pwszPos++;
  606. delete pwszAddress;
  607. }
  608. if ((UINT)(pwszPos - pwszBuffer) < cchBuffer)
  609. {
  610. *pwszPos = L'\0';
  611. }
  612. if (0 == cAddrs)
  613. {
  614. // No addresses in the string, so insure that the string returned is L"\0\0"
  615. pwszPos[1] = L'\0';
  616. }
  617. delete [] pAddresses;
  618. }
  619. else
  620. {
  621. ERROR_OUT(("DCRNCConference::GetLocalAddressList: GetLocalAddressList failed, hr=0x%x", (UINT) hr));
  622. }
  623. DebugExitHRESULT(DCRNCConference::GetLocalAddressList, hr);
  624. return hr;
  625. }
  626. STDMETHODIMP_(UINT) DCRNCConference::
  627. GetParentNodeID(void)
  628. {
  629. DebugEntry(DCRNCConference::GetConferenceID);
  630. InterfaceEntry();
  631. GCCNodeID nidParent = 0;
  632. g_pIT120ControlSap->GetParentNodeID(m_nConfID, &nidParent);
  633. DebugExitINT(DCRNCConference::GetConferenceID, (UINT) nidParent);
  634. return (UINT) nidParent;
  635. }
  636. CLogicalConnection * DCRNCConference::
  637. GetConEntry ( ConnectionHandle hInviteIndConn )
  638. {
  639. CLogicalConnection *pConEntry = NULL;
  640. m_ConnList.Reset();
  641. while (NULL != (pConEntry = m_ConnList.Iterate()))
  642. {
  643. if (pConEntry->GetInviteReqConnHandle() == hInviteIndConn)
  644. {
  645. break;
  646. }
  647. }
  648. return pConEntry;
  649. }
  650. CLogicalConnection * DCRNCConference::
  651. GetConEntry ( LPSTR pszNodeAddress )
  652. {
  653. CLogicalConnection *pConEntry = NULL;
  654. m_ConnList.Reset();
  655. while (NULL != (pConEntry = m_ConnList.Iterate()))
  656. {
  657. if (0 == ::lstrcmpA(pConEntry->GetNodeAddress(), pszNodeAddress))
  658. {
  659. break;
  660. }
  661. }
  662. return pConEntry;
  663. }
  664. CLogicalConnection * DCRNCConference::
  665. GetConEntryByNodeID ( GCCNodeID nid )
  666. {
  667. CLogicalConnection *pConEntry = NULL;
  668. m_ConnList.Reset();
  669. while (NULL != (pConEntry = m_ConnList.Iterate()))
  670. {
  671. if (nid == pConEntry->GetConnectionNodeID())
  672. {
  673. break;
  674. }
  675. }
  676. return pConEntry;
  677. }
  678. void DCRNCConference::
  679. FirstRoster(void)
  680. {
  681. DebugEntry(DCRNCConference::FirstRoster);
  682. // Great! We are now in a conference and outside of any
  683. // T120 callback, so that calling back into T120 will not
  684. // deadlock applications.
  685. // Let the applications know about the conference,
  686. // and then ask for a roster update.
  687. if (m_eT120State == T120C_ST_PENDING_ROSTER_MESSAGE)
  688. {
  689. m_eT120State = T120C_ST_CONF_STARTED;
  690. NotifyConferenceComplete(NO_ERROR);
  691. RefreshRoster();
  692. }
  693. DebugExitVOID(DCRNCConference::FirstRoster);
  694. }
  695. /****************************************************************************/
  696. /* HandleGCCCallback() - see erncconf.h */
  697. /****************************************************************************/
  698. // LONCHANC: Merged to T120 Conference.
  699. /****************************************************************************/
  700. /* ValidatePassword() - Validates a join request by checking the supplied */
  701. /* password with the one set when the conference was setup. */
  702. /****************************************************************************/
  703. BOOL DCRNCConference::
  704. ValidatePassword ( GCCChallengeRequestResponse *pPasswordChallenge )
  705. {
  706. PBYTE pbPasswordChallenge = NULL;
  707. DWORD cbPasswordChallenge = 0;
  708. CHash hashObj;
  709. OSVERSIONINFO osvi;
  710. osvi.dwOSVersionInfoSize = sizeof(osvi);
  711. if (FALSE == ::GetVersionEx (&osvi))
  712. {
  713. ERROR_OUT(("GetVersionEx() failed!"));
  714. }
  715. if (!(VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && g_bRDS) &&
  716. (NULL == m_pbHashedPassword) && (NULL == m_pszNumericPassword) && (NULL == m_pwszPassword))
  717. {
  718. return TRUE;
  719. }
  720. if ((pPasswordChallenge == NULL) ||
  721. (pPasswordChallenge->password_challenge_type != GCC_PASSWORD_IN_THE_CLEAR))
  722. {
  723. return FALSE;
  724. }
  725. //
  726. // We are going to verify the password as a logon
  727. //
  728. if ( VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && g_bRDS)
  729. {
  730. BYTE InfoBuffer[1024];
  731. PTOKEN_GROUPS ptgGroups = (PTOKEN_GROUPS)InfoBuffer;
  732. HANDLE hToken;
  733. BOOL bSuccess = FALSE;
  734. DWORD dwInfoBufferSize;
  735. PSID psidAdministrators;
  736. SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
  737. CHAR lpszBuf[1024];
  738. ASSERT(NULL != pPasswordChallenge->u.password_in_the_clear.text_string);
  739. WideCharToMultiByte( CP_ACP, 0,
  740. pPasswordChallenge->u.password_in_the_clear.text_string,
  741. -1,lpszBuf,256,NULL,NULL);
  742. CHAR* lp = (CHAR *)_StrChr(lpszBuf, ':');
  743. if (NULL == lp)
  744. {
  745. ERROR_OUT(("Expected separator in logon pwd"));
  746. return FALSE;
  747. }
  748. *lp++ = '\0';
  749. CHAR* lpPw = (CHAR *)_StrChr(lp, ':');
  750. if (NULL == lpPw)
  751. {
  752. ERROR_OUT(("Expected 2nd separator in logon pwd"));
  753. return FALSE;
  754. }
  755. *lpPw++ = '\0';
  756. if (0 == strlen(lpPw))
  757. {
  758. WARNING_OUT(("Short password in logon pwd"));
  759. return FALSE;
  760. }
  761. bSuccess = LogonUser(lpszBuf, lp, lpPw, LOGON32_LOGON_NETWORK,
  762. LOGON32_PROVIDER_DEFAULT, &hToken);
  763. if (!bSuccess)
  764. {
  765. WARNING_OUT(("LogonUser failed %d", GetLastError()));
  766. return FALSE;
  767. }
  768. if( !AllocateAndInitializeSid(&siaNtAuthority, 2,
  769. SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS,
  770. 0,0,0,0,0,0, &psidAdministrators ))
  771. {
  772. ERROR_OUT(("Error getting admin group sid: %d", GetLastError()));
  773. return FALSE;
  774. }
  775. // assume that we don't find the admin SID.
  776. bSuccess = FALSE;
  777. if (!CheckTokenMembership(hToken, psidAdministrators, &bSuccess))
  778. {
  779. ERROR_OUT(("Error checking token membership: %d", GetLastError()));
  780. bSuccess = FALSE;
  781. }
  782. FreeSid(psidAdministrators);
  783. //
  784. // If this worked there is no need to go on
  785. //
  786. if ( bSuccess )
  787. return TRUE;
  788. //
  789. // Check for group membership in the RDS users group on
  790. // the local machine.
  791. //
  792. ASSERT(FALSE == bSuccess);
  793. DWORD cbSid = 0;
  794. DWORD cbDomain = 0;
  795. SID_NAME_USE SidNameUse = SidTypeGroup;
  796. if ( LookupAccountName ( NULL, SZRDSGROUP, NULL, &cbSid,
  797. NULL, &cbDomain, &SidNameUse )
  798. || ERROR_INSUFFICIENT_BUFFER == GetLastError() )
  799. {
  800. PSID pSid = new BYTE[cbSid];
  801. LPTSTR lpszDomain = new TCHAR[cbDomain];
  802. if ( pSid && lpszDomain )
  803. {
  804. if ( LookupAccountName ( NULL, SZRDSGROUP, pSid,
  805. &cbSid, lpszDomain, &cbDomain, &SidNameUse ))
  806. {
  807. //
  808. // Make sure what we found is a group
  809. //
  810. if ( SidTypeGroup == SidNameUse ||
  811. SidTypeAlias == SidNameUse )
  812. {
  813. if (!CheckTokenMembership(hToken, pSid, &bSuccess))
  814. {
  815. ERROR_OUT(("Error checking token membership: %d", GetLastError()));
  816. bSuccess = FALSE;
  817. }
  818. }
  819. else
  820. {
  821. WARNING_OUT(("SZRDSGROUP was not a group or alias? its a %d",
  822. SidNameUse ));
  823. }
  824. }
  825. else
  826. {
  827. ERROR_OUT(("LookupAccountName (2) failed: %d",
  828. GetLastError()));
  829. }
  830. }
  831. else
  832. {
  833. ERROR_OUT(("Alloc of sid or domain failed"));
  834. }
  835. delete pSid;
  836. delete lpszDomain;
  837. }
  838. else
  839. {
  840. WARNING_OUT(("LookupAccountName (1) failed: %d", GetLastError()));
  841. }
  842. return bSuccess;
  843. }
  844. //
  845. // We are going to hash the password and compare it to the
  846. // stored hash
  847. //
  848. if (m_pbHashedPassword != NULL)
  849. {
  850. if (NULL != pPasswordChallenge->u.password_in_the_clear.text_string)
  851. {
  852. cbPasswordChallenge = hashObj.GetHashedData((LPBYTE)pPasswordChallenge->u.password_in_the_clear.text_string,
  853. sizeof(WCHAR)*lstrlenW(pPasswordChallenge->u.password_in_the_clear.text_string),
  854. (void **) &pbPasswordChallenge);
  855. }
  856. else if (NULL != pPasswordChallenge->u.password_in_the_clear.numeric_string)
  857. {
  858. int cch = lstrlenA((PSTR)pPasswordChallenge->u.password_in_the_clear.numeric_string);
  859. LPWSTR lpwszNumPassword = new WCHAR[cch+1];
  860. MultiByteToWideChar(CP_ACP, 0, (PSTR)pPasswordChallenge->u.password_in_the_clear.numeric_string,
  861. -1, lpwszNumPassword, cch+1);
  862. int cwch = lstrlenW(lpwszNumPassword);
  863. cbPasswordChallenge = hashObj.GetHashedData((LPBYTE)lpwszNumPassword, sizeof(WCHAR)*lstrlenW(lpwszNumPassword), (void **) &pbPasswordChallenge);
  864. delete []lpwszNumPassword;
  865. }
  866. else
  867. {
  868. return FALSE;
  869. }
  870. if (m_cbHashedPassword != cbPasswordChallenge) return FALSE;
  871. if (0 == memcmp(m_pbHashedPassword, pbPasswordChallenge, cbPasswordChallenge))
  872. {
  873. return TRUE;
  874. }
  875. else
  876. {
  877. return FALSE;
  878. }
  879. }
  880. else if (m_pwszPassword != NULL)
  881. {
  882. // We have a text password
  883. if ((pPasswordChallenge->u.password_in_the_clear.text_string == NULL) ||
  884. (0 != ::My_strcmpW(m_pwszPassword,
  885. pPasswordChallenge->u.password_in_the_clear.text_string)))
  886. {
  887. return FALSE;
  888. }
  889. else
  890. {
  891. return TRUE;
  892. }
  893. }
  894. else
  895. {
  896. // We have a numeric password
  897. if ((pPasswordChallenge->u.password_in_the_clear.numeric_string == NULL) ||
  898. (::lstrcmpA(m_pszNumericPassword,
  899. (PSTR) pPasswordChallenge->u.password_in_the_clear.numeric_string)))
  900. {
  901. return FALSE;
  902. }
  903. else
  904. {
  905. return TRUE;
  906. }
  907. }
  908. }
  909. /****************************************************************************/
  910. /* Join() - see erncconf.h */
  911. /****************************************************************************/
  912. HRESULT DCRNCConference::
  913. Join
  914. (
  915. LPSTR pszNodeAddress,
  916. PUSERDATAINFO pInfo,
  917. UINT nInfo,
  918. LPCWSTR _wszPassword
  919. )
  920. {
  921. HRESULT hr = NO_ERROR;
  922. DebugEntry(DCRNCConference::Join);
  923. /*
  924. * Set the password that will be used by the JoinWrapper() method.
  925. * The password will be deleted after the Join is complete.
  926. * The m_pwszPassword member is only set for the top providers
  927. * protecting conferences.
  928. */
  929. if (! ::IsEmptyStringW (_wszPassword))
  930. {
  931. // Store the password; we will need it later
  932. m_pwszPassword = ::My_strdupW(_wszPassword);
  933. if (NULL == m_pwszPassword)
  934. {
  935. hr = UI_RC_OUT_OF_MEMORY;
  936. }
  937. }
  938. /************************************************************************/
  939. /* SFR 6206. The apps treat joining a conference at a remote site as */
  940. /* an "incoming" call. (i.e they discard any local data and accept the */
  941. /* msgs/WB contents from the conference we are joining). */
  942. /************************************************************************/
  943. if (NO_ERROR == hr)
  944. {
  945. m_fIncoming = TRUE;
  946. hr = StartConnection(pszNodeAddress,
  947. CONF_CON_PENDING_JOIN,
  948. pInfo,
  949. nInfo,
  950. m_fSecure);
  951. }
  952. if (NO_ERROR != hr)
  953. {
  954. ERROR_OUT(("Error starting connection"));
  955. }
  956. /************************************************************************/
  957. /* We now sit and wait for the connection to complete before */
  958. /* continuing. */
  959. /************************************************************************/
  960. DebugExitHRESULT(DCRNCConference::Join, hr);
  961. return hr;
  962. }
  963. /****************************************************************************/
  964. /* NotifyConferenceComplete() - the generic conference has finished its */
  965. /* attempt to start. */
  966. /****************************************************************************/
  967. void DCRNCConference::
  968. NotifyConferenceComplete ( HRESULT hr )
  969. {
  970. DebugEntry(DCRNCConference::NotifyConferenceComplete);
  971. /************************************************************************/
  972. /* If the attempt fails, action depends on whether this is the first or */
  973. /* second attempt. */
  974. /************************************************************************/
  975. if (NO_ERROR != hr)
  976. {
  977. TRACE_OUT(("Attempt to start failed"));
  978. // LONCHANC: please do not remove this chunk of code.
  979. #ifdef ENABLE_START_REMOTE
  980. if (m_eState == CONF_ST_PENDING_START_REMOTE_FIRST)
  981. {
  982. TRACE_OUT(("Try second conference type"));
  983. StartSecondConference(hr);
  984. return;
  985. }
  986. #endif // ENABLE_START_REMOTE
  987. }
  988. else
  989. {
  990. TRACE_OUT(("Conference started OK."));
  991. m_eState = CONF_ST_STARTED;
  992. }
  993. g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
  994. DebugExitVOID(DCRNCConference::NotifyConferenceComplete);
  995. }
  996. /****************************************************************************/
  997. /* NotifyConnectionComplete() - see erncconf.h */
  998. /****************************************************************************/
  999. HRESULT DCRNCConference::
  1000. NotifyConnectionComplete
  1001. (
  1002. CLogicalConnection *pConEntry,
  1003. HRESULT hr
  1004. )
  1005. {
  1006. DebugEntry(DCRNCConference::NotifyConnectionComplete);
  1007. // This function is the state machine
  1008. // for bringing up a conferencing protocol.
  1009. // It manages getting the physical connection and trying
  1010. // T120 and R1.1.
  1011. // A connection has started.
  1012. // Subsequent action depends on the pending state for the connection.
  1013. // First filter out internal (success) return codes.
  1014. if (NO_ERROR != hr)
  1015. {
  1016. // Failed to get a physical connection.
  1017. WARNING_OUT(("Failed to start connection"));
  1018. if (pConEntry->GetState() != CONF_CON_PENDING_INVITE)
  1019. {
  1020. // Put the connection in a failed state before notifying the user.
  1021. // This is because notifying the user can cause GCC events to fire,
  1022. // and, in particular, a JoinRequest failure which must be ignored.
  1023. pConEntry->SetState(CONF_CON_ERROR);
  1024. g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
  1025. goto MyExit;
  1026. }
  1027. // Drop through for invite failures.
  1028. }
  1029. switch (pConEntry->GetState())
  1030. {
  1031. // LONCHANC: please do not remove this chunk of code.
  1032. #ifdef ENABLE_START_REMOTE
  1033. case CONF_CON_PENDING_START:
  1034. /****************************************************************/
  1035. /* Check we are in the correct state. */
  1036. /****************************************************************/
  1037. if ( (m_eState != CONF_ST_PENDING_CONNECTION) &&
  1038. (m_eState != CONF_ST_LOCAL_PENDING_RECREATE))
  1039. {
  1040. ERROR_OUT(("Bad state to start in..."));
  1041. goto MyExit;
  1042. }
  1043. pConEntry->SetState(CONF_CON_CONNECTED);
  1044. /****************************************************************/
  1045. /* The connection has started OK. we now try to establish */
  1046. /* either a T120 or a backlevel conference, depending on the */
  1047. /* starting order. */
  1048. /****************************************************************/
  1049. if (NO_ERROR == hr)
  1050. {
  1051. hr = StartFirstConference();
  1052. }
  1053. else
  1054. {
  1055. ERROR_OUT(("Invalid response in notify connection complete"));
  1056. }
  1057. break;
  1058. #endif // ENABLE_START_REMOTE
  1059. case CONF_CON_PENDING_JOIN:
  1060. // pConEntry->m_eState = CONF_CON_CONNECTED;
  1061. // Joining a new conference.
  1062. // Create a new generic conference and
  1063. // call its Join() entry point.
  1064. hr = NewT120Conference();
  1065. if (NO_ERROR == hr)
  1066. {
  1067. hr = JoinWrapper(pConEntry, m_pwszPassword);
  1068. // Delete the set password
  1069. if (m_pwszPassword != NULL)
  1070. {
  1071. delete m_pwszPassword;
  1072. m_pwszPassword = NULL;
  1073. }
  1074. }
  1075. else
  1076. {
  1077. ERROR_OUT(("Error %d joining conference", hr));
  1078. goto MyExit;
  1079. }
  1080. break;
  1081. case CONF_CON_PENDING_INVITE:
  1082. hr = pConEntry->InviteConnectResult(hr);
  1083. break;
  1084. default :
  1085. ERROR_OUT(("Unknown action %d", pConEntry->GetState()));
  1086. break;
  1087. }
  1088. MyExit:
  1089. DebugExitVOID(DCRNCConference::NotifyConnectionComplete);
  1090. return hr;
  1091. }
  1092. HRESULT DCRNCConference::
  1093. JoinWrapper
  1094. (
  1095. CLogicalConnection *pConEntry,
  1096. LPCWSTR _wszPassword
  1097. )
  1098. {
  1099. DebugEntry(DCRNCConference::JoinWrapper);
  1100. // Going asynchronous, so allow events to fire.
  1101. pConEntry->ReArm();
  1102. HRESULT hr = T120Join(pConEntry->GetNodeAddress(),
  1103. pConEntry->IsConnectionSecure(),
  1104. m_pwszConfName,
  1105. pConEntry->GetUserDataList(),
  1106. _wszPassword);
  1107. if (NO_ERROR == hr)
  1108. {
  1109. m_eState = CONF_ST_STARTED;
  1110. }
  1111. else
  1112. {
  1113. pConEntry->Grab();
  1114. ERROR_OUT(("Error %d joining conference", hr));
  1115. g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
  1116. }
  1117. DebugExitHRESULT(DCRNCConference::JoinWrapper, hr);
  1118. return hr;
  1119. }
  1120. /****************************************************************************/
  1121. /* NotifyRosterChanged() - see erncconf.hpp. */
  1122. /****************************************************************************/
  1123. void DCRNCConference::
  1124. NotifyRosterChanged ( PNC_ROSTER pRoster )
  1125. {
  1126. DebugEntry(DCRNCConference::NotifyRosterChanged);
  1127. // Add the conference name and ID to the roster.
  1128. pRoster->pwszConferenceName = m_pwszConfName;
  1129. pRoster->uConferenceID = m_nConfID;
  1130. /************************************************************************/
  1131. /* Pass the new roster up to the CM */
  1132. /************************************************************************/
  1133. g_pCallbackInterface->OnRosterChanged((CONF_HANDLE) this, pRoster);
  1134. DebugExitVOID(DCRNCConference::NotifyRosterChanged);
  1135. }
  1136. /****************************************************************************/
  1137. /* StartConnection - add a new connection to our connection list. */
  1138. /****************************************************************************/
  1139. HRESULT DCRNCConference::
  1140. StartConnection
  1141. (
  1142. LPSTR pszNodeAddress,
  1143. LOGICAL_CONN_STATE eAction,
  1144. PUSERDATAINFO pInfo,
  1145. UINT nInfo,
  1146. BOOL fSecure,
  1147. REQUEST_HANDLE * phRequest
  1148. )
  1149. {
  1150. HRESULT hr;
  1151. CLogicalConnection *pConEntry;
  1152. DebugEntry(DCRNCConference::StartConnection);
  1153. DBG_SAVE_FILE_LINE
  1154. pConEntry = NewLogicalConnection(eAction, NULL, pInfo, nInfo, fSecure);
  1155. if (NULL != pConEntry)
  1156. {
  1157. hr = NO_ERROR;
  1158. if (phRequest)
  1159. {
  1160. // Return context as the connection entry, if required.
  1161. *phRequest = (REQUEST_HANDLE *)pConEntry;
  1162. }
  1163. // Set node address
  1164. pConEntry->SetNodeAddress(::My_strdupA(pszNodeAddress));
  1165. //
  1166. // LONCHANC: Fire the conn-entry event.
  1167. //
  1168. hr = NotifyConnectionComplete(pConEntry, NO_ERROR);
  1169. }
  1170. else
  1171. {
  1172. hr = UI_RC_OUT_OF_MEMORY;
  1173. }
  1174. DebugExitHRESULT(DCRNCConference::StartConnection, hr);
  1175. return hr;
  1176. }
  1177. // LONCHANC: please do not remove this chunk of code.
  1178. #ifdef ENABLE_START_REMOTE
  1179. /****************************************************************************/
  1180. /* StartFirstConference() - start the first attempt to create a conference. */
  1181. /****************************************************************************/
  1182. void DCRNCConference::
  1183. StartFirstConference(void)
  1184. {
  1185. BOOL result = FALSE;
  1186. HRESULT hr;
  1187. DebugEntry(DCRNCConference::StartFirstConference);
  1188. hr = NewT120Conference();
  1189. if (NO_ERROR != hr)
  1190. {
  1191. ERROR_OUT(("Failed to create new conference"));
  1192. m_eState = CONF_ST_UNINITIALIZED;
  1193. goto MyExit;
  1194. }
  1195. /************************************************************************/
  1196. /* Call the StartRemote() entry point. */
  1197. /************************************************************************/
  1198. hr = T120StartRemote(m_pszFirstRemoteNodeAddress);
  1199. if (hr)
  1200. {
  1201. WARNING_OUT(("Failed to start remote, rc %d", hr));
  1202. goto MyExit;
  1203. }
  1204. m_eState = CONF_ST_PENDING_START_REMOTE_FIRST;
  1205. result = TRUE;
  1206. MyExit:
  1207. /************************************************************************/
  1208. /* If we failed to start the first conference, try to start the second */
  1209. /* type of conference in the starting order. */
  1210. /************************************************************************/
  1211. if (!result)
  1212. {
  1213. TRACE_OUT(("Failed to start first conference."));
  1214. StartSecondConference(hr);
  1215. }
  1216. DebugExitVOID(DCRNCConference::StartFirstConference);
  1217. }
  1218. #endif // ENABLE_START_REMOTE
  1219. // LONCHANC: please do not remove this chunk of code.
  1220. #ifdef ENABLE_START_REMOTE
  1221. /****************************************************************************/
  1222. /* StartSecondConference() - start the second attempt to create a */
  1223. /* conference. */
  1224. /****************************************************************************/
  1225. void DCRNCConference::
  1226. StartSecondConference ( HRESULT FirstConferenceStatus )
  1227. {
  1228. BOOL result = FALSE;
  1229. HRESULT hr = NO_ERROR;
  1230. DebugEntry(DCRNCConference::StartSecondConference);
  1231. hr = FirstConferenceStatus;
  1232. #if 0 // LONCHANC: very weird code
  1233. goto MyExit;
  1234. /************************************************************************/
  1235. /* Call the StartRemote() entry point. */
  1236. /************************************************************************/
  1237. hr = T120StartRemote(m_pszFirstRemoteNodeAddress);
  1238. if (NO_ERROR != hr)
  1239. {
  1240. WARNING_OUT(("Failed to start remote, rc %d", hr));
  1241. goto MyExit;
  1242. }
  1243. m_eState = CONF_ST_PENDING_START_REMOTE_SECOND;
  1244. result = TRUE;
  1245. MyExit:
  1246. #endif // 0
  1247. /************************************************************************/
  1248. /* If we have failed to start any type of conference, tell CM about it. */
  1249. /************************************************************************/
  1250. if (!result)
  1251. {
  1252. TRACE_OUT(("Failed to start Second conference."));
  1253. g_pNCConfMgr->NotifyConferenceComplete(this, m_fIncoming, hr);
  1254. }
  1255. DebugExitVOID(DCRNCConference::StartSecondConference);
  1256. }
  1257. #endif // ENABLE_START_REMOTE
  1258. /****************************************************************************/
  1259. /* StartLocal() - see erncconf.h */
  1260. /****************************************************************************/
  1261. HRESULT DCRNCConference::
  1262. StartLocal ( LPCWSTR _wszPassword, PBYTE pbHashedPassword, DWORD cbHashedPassword)
  1263. {
  1264. HRESULT hr = NO_ERROR;
  1265. DebugEntry(DCRNCConference::StartLocal);
  1266. /*
  1267. * Set the password that will be used to protect the conference.
  1268. * against unauthorized Join requests.
  1269. * The password is only set for the top providers
  1270. * protecting conferences.
  1271. * If the password is a number it will be stored in m_pszNumericPassword.
  1272. * Otherwise, it will be stored in m_pwszPassword.
  1273. */
  1274. if (NULL != pbHashedPassword)
  1275. {
  1276. m_pbHashedPassword = new BYTE[cbHashedPassword];
  1277. if (NULL == m_pbHashedPassword)
  1278. {
  1279. hr = UI_RC_OUT_OF_MEMORY;
  1280. }
  1281. else
  1282. {
  1283. memcpy(m_pbHashedPassword, pbHashedPassword, cbHashedPassword);
  1284. m_cbHashedPassword = cbHashedPassword;
  1285. }
  1286. }
  1287. else if (! ::IsEmptyStringW(_wszPassword))
  1288. {
  1289. if (::UnicodeIsNumber(_wszPassword))
  1290. {
  1291. m_pszNumericPassword = ::UnicodeToAnsi(_wszPassword);
  1292. if (m_pszNumericPassword == NULL)
  1293. {
  1294. hr = UI_RC_OUT_OF_MEMORY;
  1295. }
  1296. }
  1297. else
  1298. {
  1299. m_pwszPassword = ::My_strdupW(_wszPassword);
  1300. if (NULL == m_pwszPassword)
  1301. {
  1302. hr = UI_RC_OUT_OF_MEMORY;
  1303. }
  1304. }
  1305. }
  1306. /************************************************************************/
  1307. /* Dont need to bother getting a physical connection. Just create a */
  1308. /* new T120 conference and call its StartLocal() entry point */
  1309. /************************************************************************/
  1310. if (NO_ERROR == hr)
  1311. {
  1312. hr = NewT120Conference();
  1313. if (NO_ERROR == hr)
  1314. {
  1315. hr = T120StartLocal(m_fSecure);
  1316. if (NO_ERROR == hr)
  1317. {
  1318. m_eState = CONF_ST_PENDING_T120_START_LOCAL;
  1319. }
  1320. }
  1321. }
  1322. DebugExitHRESULT(DCRNCConference::StartLocal, hr);
  1323. return hr;
  1324. }
  1325. // LONCHANC: please do not remove this chunk of code.
  1326. #ifdef ENABLE_START_REMOTE
  1327. /****************************************************************************/
  1328. /* StartRemote() - see erncconf.h */
  1329. /****************************************************************************/
  1330. HRESULT DCRNCConference::
  1331. StartRemote ( LPSTR pszNodeAddress )
  1332. {
  1333. HRESULT hr;
  1334. DebugEntry(DCRNCConference::StartRemote);
  1335. /************************************************************************/
  1336. /* Store the node details */
  1337. /************************************************************************/
  1338. m_pszFirstRemoteNodeAddress = ::My_strdupA(pszNodeAddress);
  1339. if (NULL != m_pszFirstRemoteNodeAddress)
  1340. {
  1341. /************************************************************************/
  1342. /* We need to set the conference state before trying to start a new */
  1343. /* connection - the connection may synchronously call us back and we */
  1344. /* want to be able to handle the callback correctly. */
  1345. /************************************************************************/
  1346. m_eState = CONF_ST_PENDING_CONNECTION;
  1347. /************************************************************************/
  1348. /* Start a new physical connection. */
  1349. /************************************************************************/
  1350. hr = StartConnection(m_pszFirstRemoteNodeAddress, CONF_CON_PENDING_START, NULL, NULL);
  1351. if (NO_ERROR != hr)
  1352. {
  1353. ERROR_OUT(("Error adding connection"));
  1354. m_eState = CONF_ST_UNINITIALIZED;
  1355. }
  1356. /************************************************************************/
  1357. /* We now sit and wait for the connection to complete before */
  1358. /* continuing. */
  1359. /************************************************************************/
  1360. }
  1361. else
  1362. {
  1363. ERROR_OUT(("DCRNCConference::StartRemote: can't duplicate node address"));
  1364. hr = UI_RC_OUT_OF_MEMORY;
  1365. m_eState = CONF_ST_UNINITIALIZED;
  1366. }
  1367. DebugExitHRESULT(DCRNCConference::StartRemote, hr);
  1368. return hr;
  1369. }
  1370. #endif // ENABLE_START_REMOTE
  1371. /****************************************************************************/
  1372. /* StartIncoming() - see erncconf.h */
  1373. /****************************************************************************/
  1374. HRESULT DCRNCConference::
  1375. StartIncoming(void)
  1376. {
  1377. DebugEntry(DCRNCConference::StartIncoming);
  1378. /************************************************************************/
  1379. /* Set the incoming flag. */
  1380. /************************************************************************/
  1381. m_fIncoming = TRUE;
  1382. /************************************************************************/
  1383. /* Create a new T120 conference and call its StartIncoming entry point. */
  1384. /************************************************************************/
  1385. HRESULT hr = NewT120Conference();
  1386. if (NO_ERROR == hr)
  1387. {
  1388. m_eState = CONF_ST_STARTED;
  1389. }
  1390. else
  1391. {
  1392. WARNING_OUT(("Failed to create new local conference"));
  1393. }
  1394. DebugExitHRESULT(DCRNCConference::StartIncoming, hr);
  1395. return hr;
  1396. }
  1397. CLogicalConnection::
  1398. CLogicalConnection
  1399. (
  1400. PCONFERENCE pConf,
  1401. LOGICAL_CONN_STATE eAction,
  1402. ConnectionHandle hConnection,
  1403. PUSERDATAINFO pInfo,
  1404. UINT nInfo,
  1405. BOOL fSecure
  1406. )
  1407. :
  1408. CRefCount(MAKE_STAMP_ID('C','L','N','E')),
  1409. m_pszNodeAddress(NULL),
  1410. m_eState(eAction),
  1411. m_pConf(pConf),
  1412. m_nidConnection(0),
  1413. m_hInviteReqConn(hConnection),
  1414. m_hConnection(hConnection),
  1415. m_pLocalAddress(NULL),
  1416. m_fEventGrabbed(FALSE),
  1417. m_fSecure(fSecure)
  1418. {
  1419. DebugEntry(CLogicalConnection::CLogicalConnection);
  1420. if(nInfo)
  1421. {
  1422. for (UINT i = 0 ; i < nInfo; i++, pInfo++)
  1423. {
  1424. m_UserDataInfoList.AddUserData(pInfo->pGUID, pInfo->cbData, pInfo->pData);
  1425. }
  1426. }
  1427. if ((eAction == CONF_CON_INVITED) ||
  1428. (eAction == CONF_CON_JOINED))
  1429. {
  1430. Grab(); // No events to fire.
  1431. }
  1432. DebugExitVOID(CLogicalConnection::CLogicalConnection);
  1433. }
  1434. CLogicalConnection::
  1435. ~CLogicalConnection(void)
  1436. {
  1437. DebugEntry(CLogicalConnection::~CLogicalConnection);
  1438. ASSERT((m_eState == CONF_CON_CONNECTED) ||
  1439. (m_eState == CONF_CON_ERROR));
  1440. delete m_pszNodeAddress;
  1441. DebugExitVOID(CLogicalConnection::~CLogicalConnection);
  1442. }
  1443. BOOL CLogicalConnection::
  1444. NewLocalAddress(void)
  1445. {
  1446. BOOL bNewAddress;
  1447. m_pConf->AddLocalAddress(m_hConnection, &bNewAddress, &m_pLocalAddress);
  1448. return bNewAddress;
  1449. }
  1450. HRESULT CLogicalConnection::
  1451. InviteConnectResult ( HRESULT hr )
  1452. {
  1453. DebugEntry(CLogicalConnection::InviteConnectResult);
  1454. if (NO_ERROR == hr)
  1455. {
  1456. /****************************************************************/
  1457. /* Check the state - we should be fully initialized and have a */
  1458. /* generic conference by this stage. */
  1459. /****************************************************************/
  1460. if (m_pConf->m_eState != CONF_ST_STARTED)
  1461. {
  1462. ERROR_OUT(("Bad state %d", m_pConf->m_eState));
  1463. hr = UI_NO_SUCH_CONFERENCE;
  1464. }
  1465. else
  1466. {
  1467. // Now have a connection to the conference, so go do invite.
  1468. // Note that this may not be the only invite if the user invites
  1469. // several people into the conference before the connection is up.
  1470. ReArm(); // So that connection going down fires off event handling
  1471. hr = m_pConf->T120Invite(m_pszNodeAddress,
  1472. m_fSecure,
  1473. &m_UserDataInfoList,
  1474. &m_hInviteReqConn);
  1475. if (NO_ERROR != hr)
  1476. {
  1477. Grab();
  1478. }
  1479. }
  1480. }
  1481. if (NO_ERROR != hr)
  1482. {
  1483. InviteComplete(hr);
  1484. }
  1485. DebugExitHRESULT(CLogicalConnection::InviteConnectResult, hr);
  1486. return hr;
  1487. }
  1488. void DCRNCConference::
  1489. InviteComplete
  1490. (
  1491. ConnectionHandle hInviteReqConn,
  1492. HRESULT result,
  1493. PT120PRODUCTVERSION pVersion
  1494. )
  1495. {
  1496. CLogicalConnection * pConEntry;
  1497. DebugEntry(DCRNCConference::InviteComplete);
  1498. pConEntry = GetConEntry(hInviteReqConn);
  1499. if (pConEntry == NULL)
  1500. {
  1501. ERROR_OUT(("Unable to match invite response with request"));
  1502. return;
  1503. }
  1504. pConEntry->SetConnectionHandle(hInviteReqConn);
  1505. pConEntry->InviteComplete(result, pVersion);
  1506. DebugExitVOID(DCRNCConference::InviteComplete);
  1507. }
  1508. HRESULT CLocalAddressList::
  1509. AddLocalAddress
  1510. (
  1511. ConnectionHandle connection_handle,
  1512. BOOL *pbNewAddress,
  1513. CLocalAddress **ppLocalAddrToRet
  1514. )
  1515. {
  1516. HRESULT hr = UI_RC_OUT_OF_MEMORY;
  1517. CLocalAddress * pLocalAddress = NULL;
  1518. char szLocalAddress[64];
  1519. int nLocalAddress = sizeof(szLocalAddress);
  1520. DebugEntry(CLocalAddressList::AddLocalAddress);
  1521. *pbNewAddress = FALSE;
  1522. ASSERT (g_pMCSController != NULL);
  1523. if (g_pMCSController->GetLocalAddress (connection_handle, szLocalAddress,
  1524. &nLocalAddress)) {
  1525. DBG_SAVE_FILE_LINE
  1526. pLocalAddress = new CLocalAddress(szLocalAddress);
  1527. if (pLocalAddress) {
  1528. if (!IS_EMPTY_STRING(pLocalAddress->m_pszLocalAddress)) {
  1529. BOOL fFound = FALSE;
  1530. CLocalAddress *p;
  1531. Reset();
  1532. while (NULL != (p = Iterate()))
  1533. {
  1534. if (0 == ::lstrcmpA(p->m_pszLocalAddress, szLocalAddress))
  1535. {
  1536. fFound = TRUE;
  1537. break;
  1538. }
  1539. }
  1540. if (! fFound)
  1541. {
  1542. ASSERT(NULL == p);
  1543. Append(pLocalAddress);
  1544. }
  1545. else
  1546. {
  1547. ASSERT(NULL != p);
  1548. pLocalAddress->Release();
  1549. (pLocalAddress = p)->AddRef();
  1550. }
  1551. hr = NO_ERROR;
  1552. }
  1553. else
  1554. {
  1555. pLocalAddress->Release(); // Remove when no longer referenced
  1556. pLocalAddress = NULL;
  1557. }
  1558. }
  1559. }
  1560. *ppLocalAddrToRet = pLocalAddress;
  1561. DebugExitHRESULT(CLocalAddressList::AddLocalAddress, hr);
  1562. return hr;
  1563. }
  1564. HRESULT CLocalAddressList::
  1565. GetLocalAddressList
  1566. (
  1567. UINT *pnAddresses,
  1568. LPCSTR **ppaAddresses
  1569. )
  1570. {
  1571. CLocalAddress * pAddress;
  1572. LPCSTR * pConnection;
  1573. LPCSTR * apConn = NULL;
  1574. HRESULT hr = NO_ERROR;
  1575. DebugEntry(CLocalAddressList::GetLocalAddressList);
  1576. if (! IsEmpty())
  1577. {
  1578. DBG_SAVE_FILE_LINE
  1579. if (NULL == (apConn = new LPCSTR[GetCount()]))
  1580. {
  1581. hr = UI_RC_OUT_OF_MEMORY;
  1582. }
  1583. }
  1584. if (NULL == apConn)
  1585. {
  1586. *pnAddresses = 0;
  1587. }
  1588. else
  1589. {
  1590. hr = NO_ERROR;
  1591. *pnAddresses = GetCount();
  1592. pConnection = apConn;
  1593. Reset();
  1594. while (NULL != (pAddress = Iterate()))
  1595. {
  1596. *pConnection++ = pAddress->m_pszLocalAddress;
  1597. }
  1598. }
  1599. *ppaAddresses = apConn;
  1600. DebugExitHRESULT(CLocalAddressList::GetLocalAddressList, hr);
  1601. return hr;
  1602. }
  1603. void CLocalAddressList::
  1604. EndReference ( CLocalAddress *pLocalAddress )
  1605. {
  1606. DebugEntry(CLocalAddressList::EndReference);
  1607. if (pLocalAddress->Release() == 0)
  1608. {
  1609. Remove(pLocalAddress);
  1610. }
  1611. DebugExitVOID(CLocalAddressList::EndReference);
  1612. }
  1613. CLocalAddress::CLocalAddress(PCSTR szLocalAddress)
  1614. :
  1615. CRefCount(MAKE_STAMP_ID('L','A','D','R'))
  1616. {
  1617. m_pszLocalAddress = ::My_strdupA(szLocalAddress);
  1618. }
  1619. void CLogicalConnection::
  1620. InviteComplete
  1621. (
  1622. HRESULT hrStatus,
  1623. PT120PRODUCTVERSION pVersion
  1624. )
  1625. {
  1626. DebugEntry(CLogicalConnection::InviteComplete);
  1627. // Don't want user calling us back in
  1628. // InviteConferenceResult to delete the conference
  1629. // causing this object to be deleted whilst
  1630. // in it.
  1631. AddRef();
  1632. // Should only handle an invite complete if there is one pending.
  1633. // Otherwise, this is most likely the result of entering this function
  1634. // after telling the user that the invite has failed for some other
  1635. // reason (e.g. a physical connection going down).
  1636. // In these cases, just ignore the invite complete event.
  1637. if (m_eState == CONF_CON_PENDING_INVITE)
  1638. {
  1639. // Invite complete will generate an event, so grab it.
  1640. Grab();
  1641. if (hrStatus)
  1642. {
  1643. m_eState = CONF_CON_ERROR;
  1644. }
  1645. else
  1646. {
  1647. m_eState = CONF_CON_CONNECTED;
  1648. if (NewLocalAddress())
  1649. {
  1650. m_pConf->AnnouncePresence();
  1651. }
  1652. }
  1653. g_pCallbackInterface->OnInviteResult(
  1654. (CONF_HANDLE) m_pConf,
  1655. (REQUEST_HANDLE) this,
  1656. m_nidConnection,
  1657. hrStatus,
  1658. pVersion);
  1659. if (hrStatus)
  1660. {
  1661. // Remove conentry from conference if invite fails.
  1662. Delete(hrStatus);
  1663. }
  1664. }
  1665. Release();
  1666. DebugExitVOID(CLogicalConnection::InviteComplete);
  1667. }
  1668. void CLogicalConnection::
  1669. Delete ( HRESULT hrReason )
  1670. {
  1671. DebugEntry(CLogicalConnection::Delete);
  1672. // WARNING, WARNING, WARNING:
  1673. // This method gets re-entered on the stack.
  1674. // Note guards in code below.
  1675. if (NULL != m_pConf)
  1676. {
  1677. PCONFERENCE pThisConf = m_pConf;
  1678. PCONFERENCE pConfToFree = NULL;
  1679. m_pConf = NULL;
  1680. // The connection is going away, so remove the reference to the
  1681. // associated local connection (if any).
  1682. if (NULL != m_pLocalAddress)
  1683. {
  1684. pThisConf->EndReference(m_pLocalAddress);
  1685. m_pLocalAddress = NULL;
  1686. }
  1687. if (m_eState == CONF_CON_INVITED)
  1688. {
  1689. // The conference associated with the entry was invited into the conference,
  1690. // so remove the conference and all of its connections.
  1691. m_eState = CONF_CON_ERROR; // Only do this once
  1692. pConfToFree = pThisConf;
  1693. }
  1694. // If there is a pending event on the connection,
  1695. // then try to grab it and notify the requestor
  1696. // that the request has failed.
  1697. // Note that the event handler may itself end up
  1698. // recalling this function, so it refcounts the
  1699. // CLogicalConnection to prevent it from being destructed
  1700. // too soon.
  1701. if (Grab())
  1702. {
  1703. pThisConf->NotifyConnectionComplete(this, hrReason);
  1704. }
  1705. // Set the connection state to be in error.
  1706. // Note that this is done after firing the event because
  1707. // otherwise a failed connection attempt to a disabled transport
  1708. // would cause the local conference to be destroyed by
  1709. // NotifyConnectionComplete().
  1710. m_eState = CONF_CON_ERROR;
  1711. // Sever the connection entry with the conference record.
  1712. pThisConf->m_ConnList.Remove(this);
  1713. // Now destroy the conentry once any pending event has fired -
  1714. // there is only a pending connection request or a pending
  1715. // request to join/invite/create a conference, and never both.
  1716. Release();
  1717. if (NULL != pConfToFree)
  1718. {
  1719. g_pNCConfMgr->RemoveConference(pConfToFree);
  1720. }
  1721. }
  1722. DebugExitVOID(CLogicalConnection::Delete);
  1723. }
  1724. BOOL FindSocketNumber(DWORD nid, SOCKET * socket_number)
  1725. {
  1726. (*socket_number) = 0;
  1727. ASSERT(g_pNCConfMgr != NULL);
  1728. return g_pNCConfMgr->FindSocketNumber((GCCNodeID) nid, socket_number);
  1729. }
  1730. // DCRNCConference::FindSocketNumber
  1731. // Given a GCCNodeID, finds a socket number associated with that id.
  1732. // Returns TRUE if we are directly connected topology-wise to the node, FALSE if not.
  1733. BOOL DCRNCConference::
  1734. FindSocketNumber
  1735. (
  1736. GCCNodeID nid,
  1737. SOCKET *socket_number
  1738. )
  1739. {
  1740. CLogicalConnection *pConEntry;
  1741. m_ConnList.Reset();
  1742. while (NULL != (pConEntry = m_ConnList.Iterate()))
  1743. {
  1744. if (pConEntry->GetConnectionNodeID() == nid)
  1745. {
  1746. // Found it!
  1747. g_pMCSController->FindSocketNumber(pConEntry->GetConnectionHandle(), socket_number);
  1748. return TRUE;
  1749. }
  1750. }
  1751. return FALSE;
  1752. }
  1753. ULONG DCRNCConference::
  1754. GetNodeName(GCCNodeID NodeId, LPSTR pszBuffer, ULONG cbBufSize)
  1755. {
  1756. LPSTR pszName = m_NodeIdNameList.Find(NodeId);
  1757. if (pszName)
  1758. {
  1759. ::lstrcpynA(pszBuffer, pszName, cbBufSize);
  1760. return lstrlenA(pszName);
  1761. }
  1762. return 0;
  1763. }
  1764. ULONG DCRNCConference::
  1765. GetUserGUIDData(GCCNodeID NodeId, GUID *pGuid,
  1766. LPBYTE pbBuffer, ULONG cbBufSize)
  1767. {
  1768. CNCUserDataList *pUserDataList = m_UserDataList.Find(NodeId);
  1769. GCCUserData *pUserData;
  1770. if (pUserDataList)
  1771. {
  1772. pUserData = pUserDataList->GetUserGUIDData(pGuid);
  1773. if (pUserData)
  1774. {
  1775. if (pbBuffer)
  1776. {
  1777. ::CopyMemory(pbBuffer, pUserData->octet_string->value + sizeof(GUID),
  1778. min(cbBufSize, pUserData->octet_string->length - sizeof(GUID)));
  1779. }
  1780. return pUserData->octet_string->length - sizeof(GUID);
  1781. }
  1782. // The GUID is not found
  1783. }
  1784. // The NodeId is not found
  1785. return 0;
  1786. }
  1787. void DCRNCConference::
  1788. UpdateNodeIdNameListAndUserData(PGCCMessage message)
  1789. {
  1790. GCCNodeID NodeId;
  1791. LPSTR pszName;
  1792. LPWSTR pwszNodeName;
  1793. GCCNodeRecord *pNodeRecord;
  1794. PGCCConferenceRoster pConfRost;
  1795. USHORT count;
  1796. PGCCUserData pGccUserData;
  1797. USHORT count2;
  1798. CNCUserDataList *pUserDataList;
  1799. ASSERT (message->message_type == GCC_ROSTER_REPORT_INDICATION);
  1800. pConfRost = message->u.conf_roster_report_indication.conference_roster;
  1801. for (count = 0; count < pConfRost->number_of_records; count++)
  1802. {
  1803. pNodeRecord = pConfRost->node_record_list[count];
  1804. NodeId = pNodeRecord->node_id;
  1805. pwszNodeName = pNodeRecord->node_name;
  1806. pszName = m_NodeIdNameList.Find(NodeId);
  1807. if (!pszName)
  1808. {
  1809. int ccnsize = (lstrlenW(pwszNodeName) + 1) * sizeof(WCHAR);
  1810. DBG_SAVE_FILE_LINE
  1811. pszName = new char[ccnsize];
  1812. if (pszName)
  1813. {
  1814. if (WideCharToMultiByte(CP_ACP, 0, pwszNodeName, -1, pszName, ccnsize, NULL, NULL))
  1815. {
  1816. m_NodeIdNameList.Append(NodeId, pszName);
  1817. }
  1818. else
  1819. {
  1820. ERROR_OUT(("ConfMgr::UpdateNodeIdNameList: cannot convert unicode node name"));
  1821. }
  1822. }
  1823. else
  1824. {
  1825. ERROR_OUT(("ConfMgr::UpdateNodeIdNameList: cannot duplicate unicode node name"));
  1826. }
  1827. }
  1828. for (count2 = 0; count2 < pNodeRecord->number_of_user_data_members; count2++)
  1829. {
  1830. pGccUserData = pNodeRecord->user_data_list[count2];
  1831. if (pGccUserData->octet_string->length <= sizeof(GUID))
  1832. continue; // not real user data
  1833. pUserDataList = m_UserDataList.Find(NodeId);
  1834. if (!pUserDataList)
  1835. {
  1836. DBG_SAVE_FILE_LINE
  1837. pUserDataList = new CNCUserDataList;
  1838. m_UserDataList.Append(NodeId, pUserDataList);
  1839. }
  1840. pUserDataList->AddUserData((GUID *)pGccUserData->octet_string->value,
  1841. pGccUserData->octet_string->length - sizeof(GUID),
  1842. pGccUserData->octet_string->value + sizeof(GUID));
  1843. }
  1844. }
  1845. }