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.

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