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.

3284 lines
82 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. rndndnc.cpp
  5. Abstract:
  6. Implementation for CNDNCDirectory class that handles
  7. Non-Domain NC (Whistler ILS) access.
  8. --*/
  9. #include "stdafx.h"
  10. #include <limits.h>
  11. #include <ntdsapi.h>
  12. #include "rndcommc.h"
  13. #include "rndndnc.h"
  14. #include "rndldap.h"
  15. #include "rndcnf.h"
  16. #include "rndcoll.h"
  17. //
  18. // These are the names of the attributes in the schema.
  19. //
  20. const WCHAR * const CNDNCDirectory::s_RTConferenceAttributes[] =
  21. {
  22. L"advertisingScope", // not used for NDNCs
  23. L"msTAPI-ConferenceBlob",
  24. L"generalDescription", // not used for NDNCs
  25. L"isEncrypted", // not used for NDNCs
  26. L"msTAPI-uid",
  27. L"originator", // not used for NDNCs
  28. L"msTAPI-ProtocolId",
  29. L"startTime",
  30. L"stopTime",
  31. L"subType", // not used for NDNCs
  32. L"URL" // not used for NDNCs
  33. };
  34. const WCHAR * const CNDNCDirectory::s_RTPersonAttributes[] =
  35. {
  36. L"cn",
  37. L"telephoneNumber", // not used for NDNCs
  38. L"msTAPI-IpAddress",
  39. L"msTAPI-uid"
  40. };
  41. /////////////////////////////////////////////////////////////////////////////
  42. /////////////////////////////////////////////////////////////////////////////
  43. HRESULT CNDNCDirectory::FinalConstruct(void)
  44. {
  45. LOG((MSP_TRACE, "CNDNCDirectory::FinalConstruct - enter"));
  46. HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
  47. & m_pFTM );
  48. if ( FAILED(hr) )
  49. {
  50. LOG((MSP_INFO, "CNDNCDirectory::FinalConstruct - "
  51. "create FTM returned 0x%08x; exit", hr));
  52. return hr;
  53. }
  54. LOG((MSP_TRACE, "CNDNCDirectory::FinalConstruct - exit S_OK"));
  55. return S_OK;
  56. }
  57. /////////////////////////////////////////////////////////////////////////////
  58. // private functions
  59. /////////////////////////////////////////////////////////////////////////////
  60. /////////////////////////////////////////////////////////////////////////////
  61. //
  62. // CrackDnsName()
  63. // private helper method
  64. //
  65. // This method converts a string from DNS format to DC format. This method
  66. // allocates the output string using "new" and the caller
  67. // is responsible for freeing the output string using "delete".
  68. //
  69. // input example: "ntdsdc3.ntdev.microsoft.com"
  70. // output example: "DC=ntdsdc3,DC=ntdev,DC=microsoft,DC=com"
  71. //
  72. HRESULT CNDNCDirectory::CrackDnsName(
  73. IN WCHAR * pDnsName,
  74. OUT WCHAR ** ppDcName
  75. )
  76. {
  77. LOG((MSP_INFO, "CrackDnsName: enter"));
  78. _ASSERTE( ! IsBadStringPtr( pDnsName, (UINT) -1 ) );
  79. _ASSERTE( ! IsBadWritePtr( pDnsName, sizeof(WCHAR *) ) );
  80. //
  81. // Construct a copy of the DNS name with a slash ('/') appended to it.
  82. //
  83. WCHAR * pCanonicalDnsName;
  84. pCanonicalDnsName = new WCHAR[lstrlenW(pDnsName) + 2];
  85. if ( pCanonicalDnsName == NULL )
  86. {
  87. LOG((MSP_ERROR, "CrackDnsName: "
  88. "cannot allocate canonical DNS name - "
  89. "exit E_OUTOFMEMORY"));
  90. return E_OUTOFMEMORY;
  91. }
  92. wsprintf(pCanonicalDnsName, L"%s/", pDnsName);
  93. //
  94. // Call DsCrackNames to do the conversion.
  95. // Afterwards, we no longer need the intermediate string.
  96. //
  97. LOG((MSP_INFO, "CrackDnsName: "
  98. "Attempting to crack server name: \"%S\"",
  99. pCanonicalDnsName));
  100. DWORD dwRet;
  101. DS_NAME_RESULT * pdsNameRes;
  102. dwRet = DsCrackNamesW(
  103. NULL, // no old bind handle
  104. DS_NAME_FLAG_SYNTACTICAL_ONLY, // flag: please do locally
  105. DS_CANONICAL_NAME, // have dns name
  106. DS_FQDN_1779_NAME, // want dn
  107. 1, // how many to convert
  108. &pCanonicalDnsName, // the name to convert
  109. &pdsNameRes // result of conversion
  110. );
  111. delete pCanonicalDnsName;
  112. //
  113. // Check if the conversion succeeded.
  114. //
  115. if ( dwRet != ERROR_SUCCESS )
  116. {
  117. HRESULT hr = HRESULT_FROM_ERROR_CODE( dwRet );
  118. LOG((MSP_ERROR, "CrackDnsName: "
  119. "DsCrackNames returned 0x%08x; exit 0x%08x", dwRet, hr));
  120. return hr;
  121. }
  122. if ((pdsNameRes->cItems < 1) ||
  123. (pdsNameRes->rItems == NULL) ||
  124. (pdsNameRes->rItems[0].status != DS_NAME_NO_ERROR))
  125. {
  126. DsFreeNameResult( pdsNameRes );
  127. LOG((MSP_ERROR, "CrackDnsName: "
  128. "DsCrackNames succeeded but did not return data; "
  129. "exit E_FAIL"));
  130. return E_FAIL;
  131. }
  132. //
  133. // Succeeded; return the resulting string and free the name result.
  134. //
  135. *ppDcName = new WCHAR [ lstrlenW( pdsNameRes->rItems[0].pName ) + 1];
  136. if ( (*ppDcName) == NULL )
  137. {
  138. DsFreeNameResult( pdsNameRes );
  139. LOG((MSP_ERROR, "CrackDnsName: "
  140. "failed to allocate result string - "
  141. "exit E_OUTOFMEMORY"));
  142. return E_OUTOFMEMORY;
  143. }
  144. lstrcpyW( *ppDcName, pdsNameRes->rItems[0].pName );
  145. DsFreeNameResult( pdsNameRes );
  146. LOG((MSP_INFO, "CrackDnsName: "
  147. "DsCrackNames returned %S; exit S_OK",
  148. *ppDcName));
  149. return S_OK;
  150. }
  151. HRESULT CNDNCDirectory::NDNCSetTTL(
  152. IN LDAP * pLdap,
  153. IN const WCHAR * pDN,
  154. IN DWORD dwTTL
  155. )
  156. /*++
  157. Routine Description:
  158. This function sets the TTL on a dynamic object, while enforcing
  159. the maximum settable TTL for NDNCs.
  160. Arguments:
  161. pLdap - Pointer to the LDAP connection structure.
  162. pDN - Pointer to a wide-character string specifying the DN of the object
  163. to be mofified.
  164. dwTTL - The TTL to set, in seconds.
  165. Return Value:
  166. HRESULT.
  167. --*/
  168. {
  169. if ( dwTTL > NDNC_MAX_TTL )
  170. {
  171. dwTTL = NDNC_MAX_TTL;
  172. }
  173. return ::SetTTL(pLdap, pDN, dwTTL);
  174. }
  175. HRESULT CNDNCDirectory::Init(
  176. IN const TCHAR * const strServerName,
  177. IN const WORD wPort
  178. )
  179. /*++
  180. Routine Description:
  181. Initialize this object, specifying the server name and port to use.
  182. This method fails if there is no default TAPI NDNC on this server.
  183. Arguments:
  184. strServerName - The NDNC server name.
  185. wPort - The port number.
  186. Return Value:
  187. HRESULT.
  188. --*/
  189. {
  190. LOG((MSP_TRACE, "CNDNCDirectory::Init - enter"));
  191. if ( strServerName != NULL )
  192. {
  193. //
  194. // Set the server name.
  195. //
  196. m_pServerName = new TCHAR [lstrlen(strServerName) + 1];
  197. if (m_pServerName == NULL)
  198. {
  199. LOG((MSP_ERROR, "CNDNCDirectory::Init - "
  200. "could not allocate server name - "
  201. "exit E_OUTOFMEMORY"));
  202. return E_OUTOFMEMORY;
  203. }
  204. lstrcpy(m_pServerName, strServerName);
  205. //
  206. // Look around on the server and find out where the NDNC
  207. // is. This will fail if there is no NDNC. It sets
  208. // m_pServiceDnsName, which is then used on Connect.
  209. //
  210. HRESULT hr = GetNDNCLocationFromServer( m_pServerName );
  211. if ( FAILED(hr) )
  212. {
  213. LOG((MSP_ERROR, "CNDNCDirectory::Init - "
  214. "GetNDNCLocationFromServer failed - "
  215. "exit 0x%08x", hr));
  216. return hr;
  217. }
  218. }
  219. m_wPort = wPort;
  220. LOG((MSP_TRACE, "CNDNCDirectory::Init - exit S_OK"));
  221. return S_OK;
  222. }
  223. /////////////////////////////////////////////////////////////////////////////
  224. //
  225. // CNDNCDirectory::GetNDNCLocationFromServer
  226. // private helper method
  227. //
  228. // Look around on a server and discover the default TAPI NDNC. The resulting
  229. // location is saved in m_pServiceDnsName which is then used on Connect
  230. // to go to the right place on the server.
  231. //
  232. // Parameters:
  233. // pDcServerDnsName - the DNS name that can be used to connect to the
  234. // desired DC server machine.
  235. //
  236. // Returns: HRESULT
  237. //
  238. HRESULT CNDNCDirectory::GetNDNCLocationFromServer(
  239. IN WCHAR * pDcServerDnsName
  240. )
  241. {
  242. LOG((MSP_INFO, "CNDNCDirectory::GetNDNCLocationFromServer - "
  243. "enter"));
  244. //
  245. // Connect to the DC whose name we were given.
  246. // NOTE pDcServerDnsName can be NULL, and that's ok --
  247. // it means go to the nearest DC.
  248. //
  249. HRESULT hr;
  250. LDAP * hLdap;
  251. hr = OpenLdapConnection(
  252. pDcServerDnsName,
  253. LDAP_PORT,
  254. & hLdap
  255. );
  256. if ( FAILED(hr) )
  257. {
  258. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  259. "OpenLdapConnection failed - exit 0x%08x", hr));
  260. return hr;
  261. }
  262. //
  263. // Bind. Without bindwe cannot discover the SCP
  264. //
  265. ULONG res = ldap_bind_s(hLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  266. if( LDAP_SUCCESS != res )
  267. {
  268. ldap_unbind( hLdap );
  269. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  270. "init BIND failed - exit %d", res));
  271. return GetLdapHResult(res);
  272. }
  273. //
  274. // Find the DN for this DC's domain.
  275. //
  276. WCHAR * pDomainDN;
  277. hr = GetNamingContext(
  278. hLdap,
  279. &pDomainDN
  280. );
  281. if ( FAILED(hr) )
  282. {
  283. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  284. "GetNamingContext failed - exit 0x%08x", hr));
  285. ldap_unbind( hLdap );
  286. return hr;
  287. }
  288. //
  289. // Search for the service connection point object telling us the
  290. // DNS name of the service we care about.
  291. //
  292. // First, construct the search location string. This consists of a well-known
  293. // prefix prepended to the DN obtained above.
  294. //
  295. WCHAR * pSearchLocation = new WCHAR[
  296. lstrlenW(NDNC_SERVICE_PUBLICATION_LOCATION) +
  297. lstrlenW( pDomainDN ) + 1];
  298. if ( pSearchLocation == NULL )
  299. {
  300. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  301. "canot allocate search location string - "
  302. "exit E_OUTOFMEMORY"));
  303. ldap_unbind( hLdap );
  304. delete pDomainDN;
  305. return E_OUTOFMEMORY;
  306. }
  307. wsprintf(pSearchLocation, L"%s%s", NDNC_SERVICE_PUBLICATION_LOCATION, pDomainDN);
  308. delete pDomainDN;
  309. //
  310. // Now do the actual search.
  311. //
  312. LDAPMessage * pSearchResult;
  313. PTCHAR Attributes[] = {
  314. (WCHAR *) SERVICE_DNS_NAME_ATTRIBUTE,
  315. NULL
  316. };
  317. res = DoLdapSearch (
  318. hLdap, // handle to connection structure
  319. pSearchLocation, // where to search
  320. LDAP_SCOPE_BASE, // scope of the search
  321. (WCHAR *) ANY_OBJECT_CLASS, // filter
  322. Attributes, // array of attribute names
  323. FALSE, // also return the attribute values
  324. &pSearchResult // search results
  325. );
  326. hr = GetLdapHResultIfFailed( res );
  327. delete pSearchLocation;
  328. if ( FAILED(hr) )
  329. {
  330. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  331. "connection point search failed - exit 0x%08x", hr));
  332. ldap_unbind( hLdap );
  333. return hr;
  334. }
  335. //
  336. // Parse search results and get the value of serviceDnsName.
  337. //
  338. // Step 1: Get the first object in the result set.
  339. // Note: Result of ldap_first_entry must not be explicitly freed.
  340. // Note: We ignore objects past the first one, which is fine because
  341. // we did a base-level search on just the object that we need.
  342. //
  343. LDAPMessage * pEntry;
  344. pEntry = ldap_first_entry(
  345. hLdap,
  346. pSearchResult
  347. );
  348. if ( pEntry == NULL )
  349. {
  350. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  351. "failed to get first entry in search result - "
  352. "exit E_FAIL"));
  353. ldap_msgfree( pSearchResult );
  354. ldap_unbind( hLdap );
  355. return E_FAIL;
  356. }
  357. //
  358. // Step 2: Get the values of the desired attribute for the object.
  359. //
  360. // Some attributes are multivalued so we get back an array of strings.
  361. // We only look at the first value returned for the serviceDnsName
  362. // attribute.
  363. //
  364. WCHAR ** ppServiceDnsName;
  365. ppServiceDnsName = ldap_get_values(
  366. hLdap,
  367. pEntry,
  368. (WCHAR *) SERVICE_DNS_NAME_ATTRIBUTE
  369. );
  370. ldap_unbind( hLdap );
  371. ldap_msgfree( pSearchResult );
  372. if ( ( ppServiceDnsName == NULL ) || ( ppServiceDnsName[0] == NULL ) )
  373. {
  374. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  375. "failed to get name from search result - "
  376. "exit E_FAIL"));
  377. return E_FAIL;
  378. }
  379. //
  380. // Set the service DNS name as a member variable with a nicer format.
  381. // When the app calls ITDirectory::Connect, we will try the real
  382. // connection, and during that process we will use this to determine
  383. // what container we go to for accessing the NDNC.
  384. //
  385. m_pServiceDnsName = new WCHAR [ lstrlenW(ppServiceDnsName[0]) + 1];
  386. if ( m_pServiceDnsName == NULL )
  387. {
  388. LOG((MSP_ERROR, "CNDNCDirectory::GetNDNCLocationFromServer - "
  389. "failed to allocate service DNS name member string - "
  390. "exit E_OUTOFMEMORY"));
  391. ldap_value_free( ppServiceDnsName );
  392. return E_OUTOFMEMORY;
  393. }
  394. lstrcpyW( m_pServiceDnsName, ppServiceDnsName[0] );
  395. ldap_value_free( ppServiceDnsName );
  396. LOG((MSP_INFO, "CNDNCDirectory::GetNDNCLocationFromServer - "
  397. "exit S_OK"));
  398. return S_OK;
  399. }
  400. /////////////////////////////////////////////////////////////////////////////
  401. //
  402. // CNDNCDirectory::OpenLdapConnection
  403. // private helper method
  404. //
  405. // Initiate an LDAP connection to the given port on the given server, and
  406. // return a handle to the LDAP connection context structure. This also
  407. // configures all necessary options for the connection, such as the LDAP
  408. // version and the timeout.
  409. //
  410. // Parameters:
  411. // pServerName - [in] the DNS name of the machine to connect to.
  412. // wPort - [in] the port number to use.
  413. // phLdap - [out] on success, returns a handle to the LDAP connection
  414. // context structure
  415. //
  416. // Returns: HRESULT
  417. //
  418. HRESULT CNDNCDirectory::OpenLdapConnection(
  419. IN WCHAR * pServerName,
  420. IN WORD wPort,
  421. OUT LDAP ** phLdap
  422. )
  423. {
  424. CLdapPtr hLdap = ldap_init(pServerName, wPort);
  425. BAIL_IF_NULL((LDAP*)hLdap, HRESULT_FROM_WIN32(ERROR_BAD_NETPATH));
  426. LDAP_TIMEVAL Timeout;
  427. Timeout.tv_sec = REND_LDAP_TIMELIMIT;
  428. Timeout.tv_usec = 0;
  429. DWORD res = ldap_connect((LDAP*)hLdap, &Timeout);
  430. BAIL_IF_LDAP_FAIL(res, "connect to the server.");
  431. DWORD LdapVersion = 3;
  432. res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_VERSION, &LdapVersion);
  433. BAIL_IF_LDAP_FAIL(res, "set ldap version to 3");
  434. res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_TIMELIMIT, &Timeout);
  435. BAIL_IF_LDAP_FAIL(res, "set ldap timelimit");
  436. ULONG ldapOptionOn = PtrToUlong(LDAP_OPT_ON);
  437. res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_AREC_EXCLUSIVE, &ldapOptionOn);
  438. BAIL_IF_LDAP_FAIL(res, "set ldap arec exclusive");
  439. //
  440. // Successful; return the ldap connection handle.
  441. // reset the holder so that it doesn't release anything.
  442. //
  443. *phLdap = hLdap;
  444. hLdap = NULL;
  445. return S_OK;
  446. }
  447. HRESULT CNDNCDirectory::TryServer(
  448. IN WORD Port,
  449. IN WCHAR * pServiceDnsName
  450. )
  451. /*++
  452. Routine Description:
  453. Try to connect to the NDNC server on the given port and construct
  454. the container used for subsequent operations based on the ServiceDnsName
  455. found during Init.
  456. Arguments:
  457. wPort - The port number.
  458. pServiceDnsName - DNS name of NDNC service. This is used to find the
  459. location in the tree to use for subsequent operations.
  460. Return Value:
  461. HRESULT.
  462. --*/
  463. {
  464. LOG((MSP_INFO, "CNDNCDirectory::TryServer - "
  465. "trying %S at port %d; service DNS name = %S",
  466. m_pServerName, Port, pServiceDnsName));
  467. //
  468. // Try an LDAP collection and set various options for the connection.
  469. // associate the ldap handle with the handle holder. in case of an error
  470. // and subsequent return (without being reset), the ldap handle is closed
  471. //
  472. CLdapPtr hLdap;
  473. HRESULT hr;
  474. hr = OpenLdapConnection( m_pServerName, Port, (LDAP **) & hLdap );
  475. if ( FAILED(hr) )
  476. {
  477. LOG((MSP_ERROR, "CNDNCDirectory::TryServer - "
  478. "OpenLdapConnection failed - "
  479. "exit 0x%08x", hr));
  480. return hr;
  481. }
  482. if (m_IsSsl)
  483. {
  484. DWORD res = ldap_set_option(hLdap, LDAP_OPT_SSL, LDAP_OPT_ON);
  485. BAIL_IF_LDAP_FAIL(res, "set ssl option");
  486. }
  487. //
  488. // Determine the Root DN for the NDNC.
  489. //
  490. WCHAR * pNdncRootDn;
  491. hr = CrackDnsName(pServiceDnsName, &pNdncRootDn);
  492. if ( FAILED(hr ) )
  493. {
  494. LOG((MSP_ERROR, "CNDNCDirectory::TryServer - "
  495. "could not crack service DNS name - exiting 0x%08x", hr));
  496. return hr;
  497. }
  498. //
  499. // Use the root DN for the NDNC to construct the DN for the
  500. // dynamic container.
  501. //
  502. m_pContainer =
  503. new TCHAR[
  504. lstrlen(DYNAMIC_CONTAINER) +
  505. lstrlen(pNdncRootDn) + 1
  506. ];
  507. if (m_pContainer == NULL)
  508. {
  509. LOG((MSP_ERROR, "CNDNCDirectory::TryServer - "
  510. "could not allocate container string - "
  511. "exit E_OUTOFMEMORY"));
  512. delete pNdncRootDn;
  513. return E_OUTOFMEMORY;
  514. }
  515. lstrcpy(m_pContainer, DYNAMIC_CONTAINER);
  516. lstrcat(m_pContainer, pNdncRootDn);
  517. delete pNdncRootDn;
  518. //
  519. // All done; save the connection handle.
  520. // reset the holder so that it doesn't release anything.
  521. //
  522. m_ldap = hLdap;
  523. hLdap = NULL;
  524. LOG((MSP_INFO, "CNDNCDirectory::TryServer - exiting OK"));
  525. return S_OK;
  526. }
  527. HRESULT CNDNCDirectory::MakeConferenceDN(
  528. IN TCHAR * pName,
  529. OUT TCHAR ** ppDN
  530. )
  531. /*++
  532. Routine Description:
  533. Construct the DN for a conference based on the name of the conference.
  534. Arguments:
  535. pName - The name of the conference.
  536. ppDN - The DN of the conference returned.
  537. Return Value:
  538. HRESULT.
  539. --*/
  540. {
  541. DWORD dwLen =
  542. lstrlen(m_pContainer) + lstrlen(NDNC_CONF_DN_FORMAT)
  543. + lstrlen(pName) + 1;
  544. *ppDN = new TCHAR [dwLen];
  545. BAIL_IF_NULL(*ppDN, E_OUTOFMEMORY);
  546. wsprintf(*ppDN, NDNC_CONF_DN_FORMAT, pName, m_pContainer);
  547. return S_OK;
  548. }
  549. HRESULT CNDNCDirectory::MakeUserCN(
  550. IN TCHAR * pName,
  551. IN TCHAR * pAddress,
  552. OUT TCHAR ** ppCN,
  553. OUT DWORD * pdwIP
  554. )
  555. /*++
  556. Routine Description:
  557. Construct a User's CN based on username and machine name. The machine
  558. name is resolved first the get the fully qualified DNS name. The CN is
  559. in the following format: email\DNSname.
  560. Arguments:
  561. pName - The name of the user.
  562. pAddress - The machine name.
  563. ppCN - The CN returned.
  564. pdwIP - The resolved IP address of the machine. Used later
  565. for Netmeeting. If this is NULL, then we don't care
  566. about the IP.
  567. Return Value:
  568. HRESULT.
  569. --*/
  570. {
  571. char *pchFullDNSName;
  572. if ( pdwIP == NULL )
  573. {
  574. BAIL_IF_FAIL(ResolveHostName(0, pAddress, &pchFullDNSName, NULL),
  575. "can't resolve host name");
  576. }
  577. else
  578. {
  579. // we care about the IP, so we must be publishing a user object
  580. // as opposed to refreshing or deleting. Make sure we use the
  581. // same IP as the interface that's used to reach the NDNC server.
  582. BAIL_IF_FAIL(ResolveHostName(m_dwInterfaceAddress, pAddress, &pchFullDNSName, pdwIP),
  583. "can't resolve host name (matching interface address)");
  584. }
  585. DWORD dwLen = lstrlen(DYNAMIC_USER_CN_FORMAT)
  586. + lstrlen(pName) + lstrlenA(pchFullDNSName);
  587. *ppCN = new TCHAR [dwLen + 1];
  588. BAIL_IF_NULL(*ppCN, E_OUTOFMEMORY);
  589. wsprintf(*ppCN, DYNAMIC_USER_CN_FORMAT, pName, pchFullDNSName);
  590. return S_OK;
  591. }
  592. HRESULT CNDNCDirectory::MakeUserDN(
  593. IN TCHAR * pCN,
  594. IN DWORD dwIP,
  595. OUT TCHAR ** ppDNRTPerson
  596. )
  597. /*++
  598. Routine Description:
  599. Construct the DN for a user used in the Dynamic container.
  600. Arguments:
  601. pCN - the CN of a user.
  602. ppDNRTPerson - The DN of the user in the dynamic container.
  603. Return Value:
  604. HRESULT.
  605. --*/
  606. {
  607. //
  608. // Make pUserName the user portion of the CN (user]machine).
  609. //
  610. CTstr pUserName = new TCHAR[ lstrlen(pCN) + 1 ];
  611. if ( pUserName == NULL )
  612. {
  613. BAIL_IF_FAIL(E_OUTOFMEMORY, "new TCAR");
  614. }
  615. lstrcpy( pUserName, pCN );
  616. WCHAR * pCloseBracket = wcschr( pUserName, CLOSE_BRACKET_CHARACTER );
  617. if ( pCloseBracket == NULL )
  618. {
  619. // this is not the format generated by us -- very strange indeed!
  620. BAIL_IF_FAIL(E_UNEXPECTED, "Strange format");
  621. }
  622. *pCloseBracket = NULL_CHARACTER;
  623. //
  624. // We'll use the IPAddress in cn
  625. //
  626. TCHAR szIPAddress[80];
  627. wsprintf( szIPAddress, _T("%u"), dwIP);
  628. //
  629. // Prepare the new DN
  630. //
  631. CTstr pCNIPAddress = new TCHAR[wcslen(pUserName)+1+wcslen(szIPAddress)+1];
  632. if( pCNIPAddress == NULL )
  633. {
  634. BAIL_IF_FAIL(E_OUTOFMEMORY, "new TCAR");
  635. }
  636. swprintf( pCNIPAddress, L"%s%C%s",
  637. pUserName,
  638. CLOSE_BRACKET_CHARACTER,
  639. szIPAddress
  640. );
  641. // construct the DN for RTPerson.
  642. DWORD dwLen = lstrlen(m_pContainer)
  643. + lstrlen(DYNAMIC_USER_DN_FORMAT) + lstrlen(pCNIPAddress);
  644. *ppDNRTPerson = new TCHAR [dwLen + 1];
  645. BAIL_IF_NULL(*ppDNRTPerson, E_OUTOFMEMORY);
  646. wsprintf(*ppDNRTPerson, DYNAMIC_USER_DN_FORMAT, pCNIPAddress, m_pContainer);
  647. return S_OK;
  648. }
  649. /////////////////////////////////////////////////////////////////////////////
  650. //
  651. // CNDNCDirectory::AddConferenceComplete
  652. // private helper method
  653. //
  654. // This method simply calls the appropriate LDAP operation used to send a new
  655. // or modified conference to the server.
  656. //
  657. // Parameters:
  658. // fModify - [in] TRUE if modifying, FALSE if adding
  659. // ldap - [in] handle to LDAP connection context
  660. // ppDn - [in] pointer to pointer to string specifying DN of the object
  661. // (no real reason for extra indirection here)
  662. // mods - [in] array of modifiers containing attribute values to be set
  663. //
  664. // Returns: HRESULT
  665. //
  666. HRESULT CNDNCDirectory::AddConferenceComplete(BOOL fModify,
  667. LDAP * ldap,
  668. TCHAR ** ppDN,
  669. LDAPMod ** mods)
  670. {
  671. if (fModify)
  672. {
  673. // Call the modify function to modify the object.
  674. BAIL_IF_LDAP_FAIL(DoLdapModify(FALSE, ldap, *ppDN, mods, FALSE),
  675. "modify conference");
  676. }
  677. else
  678. {
  679. // Call the add function to create the object.
  680. BAIL_IF_LDAP_FAIL(DoLdapAdd(ldap, *ppDN, mods), "add conference");
  681. }
  682. return S_OK;
  683. }
  684. HRESULT CNDNCDirectory::AddConference(
  685. IN ITDirectoryObject *pDirectoryObject,
  686. IN BOOL fModify
  687. )
  688. /*++
  689. Routine Description:
  690. Add a new conference to the NDNC server.
  691. Arguments:
  692. pDirectoryObject - a pointer to the conference.
  693. fModify - true if called by MofifyDirectoryObject
  694. false if called by AddDirectoryObject
  695. --*/
  696. {
  697. // first query the private interface for attributes.
  698. CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
  699. BAIL_IF_FAIL(
  700. pDirectoryObject->QueryInterface(
  701. IID_ITDirectoryObjectPrivate,
  702. (void **)&pObjectPrivate
  703. ),
  704. "can't get the private directory object interface");
  705. // Get the name of the conference.
  706. CBstr bName;
  707. BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
  708. "get conference name");
  709. // Construct the DN of the object.
  710. CTstr pDN;
  711. BAIL_IF_FAIL(
  712. MakeConferenceDN(bName, &pDN), "construct DN for conference"
  713. );
  714. // Get the protocol and the blob.
  715. CBstr bProtocol, bBlob;
  716. BAIL_IF_FAIL(pObjectPrivate->GetAttribute(MA_PROTOCOL, &bProtocol),
  717. "get conference protocol");
  718. BAIL_IF_FAIL(pObjectPrivate->GetAttribute(MA_CONFERENCE_BLOB, &bBlob),
  719. "get conference Blob");
  720. // Get the Security Descriptor. The pointer pSD is just a copy of a pointer
  721. // in the Conference object; the conference object retains ownership of the
  722. // data and we must be careful not to delete or modify this data.
  723. char * pSD;
  724. DWORD dwSDSize;
  725. BAIL_IF_FAIL(pObjectPrivate->GetConvertedSecurityDescriptor(&pSD, &dwSDSize),
  726. "get conference security descriptor");
  727. // Get the TTL setting.
  728. DWORD dwTTL;
  729. BAIL_IF_FAIL(pObjectPrivate->GetTTL(&dwTTL), "get conference TTL");
  730. //
  731. // Adjust the TTL setting to what we're really going to send.
  732. //
  733. if ( dwTTL == 0 )
  734. {
  735. dwTTL = m_TTL;
  736. }
  737. if ( dwTTL > NDNC_MAX_TTL )
  738. {
  739. dwTTL = NDNC_MAX_TTL;
  740. }
  741. // 5 attributes need to be published.
  742. static const DWORD DWATTRIBUTES = 5;
  743. // Fist fill the modify structures required by LDAP.
  744. LDAPMod mod[DWATTRIBUTES];
  745. LDAPMod* mods[DWATTRIBUTES + 1];
  746. DWORD dwCount = 0;
  747. // The objectclass attribute.
  748. TCHAR * objectClass[] =
  749. {(WCHAR *)NDNC_RTCONFERENCE, (WCHAR *)DYNAMICOBJECT, NULL};
  750. if (!fModify)
  751. {
  752. mod[dwCount].mod_values = objectClass;
  753. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  754. mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
  755. dwCount ++;
  756. }
  757. // The protocol attribute.
  758. TCHAR * protocol[] = {(WCHAR *)bProtocol, NULL};
  759. mod[dwCount].mod_values = protocol;
  760. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  761. mod[dwCount].mod_type = (WCHAR *)RTConferenceAttributeName(MA_PROTOCOL);
  762. dwCount ++;
  763. // The blob attribute.
  764. TCHAR * blob[] = {(WCHAR *)bBlob, NULL};
  765. mod[dwCount].mod_values = blob;
  766. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  767. mod[dwCount].mod_type =
  768. (WCHAR *)RTConferenceAttributeName(MA_CONFERENCE_BLOB);
  769. dwCount ++;
  770. // The TTL attribute. The attribute value is a DWORD in string.
  771. TCHAR strTTL[32];
  772. TCHAR * ttl[] = {strTTL, NULL};
  773. wsprintf(strTTL, _T("%d"), dwTTL );
  774. mod[dwCount].mod_values = ttl;
  775. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  776. mod[dwCount].mod_type = (WCHAR *)ENTRYTTL;
  777. dwCount ++;
  778. //
  779. // These locals should not be within the "if" below... if
  780. // they are, they will be deallocated before the function returns.
  781. //
  782. berval BerVal;
  783. berval *sd[] = {&BerVal, NULL};
  784. HRESULT hr;
  785. //
  786. // If there is a security descriptor on the local object, perhaps send it
  787. // to the server.
  788. //
  789. if ( (char*)pSD != NULL )
  790. {
  791. BOOL fSendIt = FALSE;
  792. if ( ! fModify )
  793. {
  794. //
  795. // We are trying to add the conference, so we definitely need
  796. // to send the security descriptor. Note that we even want
  797. // to send it if it hasn't changed, as we may be sending it to
  798. // some new server other than where we got it (if this conference
  799. // object was retrieved from a server in the first place).
  800. //
  801. fSendIt = TRUE;
  802. }
  803. else
  804. {
  805. //
  806. // We are trying to modify the conference, so we send the
  807. // security descriptor if it has changed.
  808. //
  809. VARIANT_BOOL fChanged;
  810. hr = pObjectPrivate->get_SecurityDescriptorIsModified( &fChanged );
  811. if ( SUCCEEDED( hr ) && ( fChanged == VARIANT_TRUE ) )
  812. {
  813. fSendIt = TRUE;
  814. }
  815. }
  816. if ( fSendIt )
  817. {
  818. BerVal.bv_len = dwSDSize;
  819. BerVal.bv_val = (char*)pSD;
  820. mod[dwCount].mod_bvalues = sd;
  821. mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
  822. mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
  823. dwCount ++;
  824. }
  825. }
  826. //
  827. // All done with the mods. Package them up and write to the server.
  828. //
  829. DWORD i;
  830. for (i = 0; i < dwCount; i ++)
  831. {
  832. mods[i] = &mod[i];
  833. }
  834. mods[i] = NULL;
  835. LOG((MSP_INFO, "%S %S", fModify ? _T("modifying") : _T("adding"), pDN));
  836. hr = AddConferenceComplete(fModify, m_ldap, &pDN, mods);
  837. if ( SUCCEEDED(hr) )
  838. {
  839. pObjectPrivate->put_SecurityDescriptorIsModified( VARIANT_FALSE );
  840. }
  841. return hr;
  842. }
  843. //////////////////////////////////////////////////////////////////////////////
  844. //
  845. // This method tests if the given ACL (security descriptor) is "safe".
  846. // "Safe" is defined as allowing the creator to modify and delete the
  847. // object; modification is tested via the TTL field.
  848. //
  849. // This test is needed to prevent leaving "ghost" objects on the server
  850. // when modifying the TTL of a newly-created conference fails because of
  851. // insfufficient access rights. This will happen if a user messes up
  852. // ACL creation or if the server does not understand the domain trustees
  853. // in the ACL (e.g., server machine is not in a domain).
  854. //
  855. // The test is performed by creating a "dummy" test object with a random
  856. // name in the container normally used for conferences. The object will
  857. // not show up in a normal conference or user enumeration because it does
  858. // not have the required attributes.
  859. //
  860. //
  861. HRESULT CNDNCDirectory::TestAclSafety(
  862. IN char * pSD,
  863. IN DWORD dwSDSize
  864. )
  865. {
  866. LOG((MSP_TRACE, "CNDNCDirectory::TestACLSafety - enter"));
  867. //
  868. // First fill in the modify structures required by LDAP.
  869. // We use only the object class and the security descriptor.
  870. // Therefore this will not show up as a valid conference
  871. // during an enumeration.
  872. //
  873. static const DWORD DWATTRIBUTES = 2;
  874. LDAPMod mod[DWATTRIBUTES];
  875. LDAPMod* mods[DWATTRIBUTES + 1];
  876. DWORD dwCount = 0;
  877. TCHAR * objectClass[] =
  878. {(WCHAR *)NDNC_RTCONFERENCE, (WCHAR *)DYNAMICOBJECT, NULL};
  879. mod[dwCount].mod_values = objectClass;
  880. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  881. mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
  882. dwCount ++;
  883. berval BerVal;
  884. berval *sd[] = {&BerVal, NULL};
  885. BerVal.bv_len = dwSDSize;
  886. BerVal.bv_val = (char*)pSD;
  887. mod[dwCount].mod_bvalues = sd;
  888. mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
  889. mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
  890. dwCount ++;
  891. DWORD i;
  892. for (i = 0; i < dwCount; i ++)
  893. {
  894. mods[i] = &mod[i];
  895. }
  896. mods[i] = NULL;
  897. //
  898. // Try to add an object in the dynamic container with the above mods.
  899. // Use a name composed of a random number printed to a string. If the
  900. // names happen to conflict with another such dummy conference, then
  901. // try again in a loop.
  902. //
  903. // Randomization is apparently per-DLL -- Sdpblb.dll does srand() but
  904. // it doesn't seem to affect rand() calls within rend.dll. We therefore do
  905. // srand( time( NULL ) ) on DLL_PROCESS_ATTACH.
  906. //
  907. HRESULT hr;
  908. int iRandomNumber;
  909. TCHAR ptszRandomNumber[30];
  910. TCHAR * pDN = NULL;
  911. do
  912. {
  913. if ( pDN != NULL )
  914. {
  915. delete pDN;
  916. }
  917. iRandomNumber = rand();
  918. wsprintf(ptszRandomNumber, _T("%d"), iRandomNumber);
  919. hr = CNDNCDirectory::MakeConferenceDN(ptszRandomNumber, &pDN);
  920. if ( FAILED(hr) )
  921. {
  922. LOG((MSP_ERROR, "CNDNCDirectory::TestACLSafety - "
  923. "test DN construction failed - exit 0x%08x", hr));
  924. return hr;
  925. }
  926. LOG((MSP_TRACE, "CNDNCDirectory::TestACLSafety - "
  927. "trying to create test object DN %S", pDN));
  928. hr = GetLdapHResultIfFailed( DoLdapAdd(m_ldap, pDN, mods) );
  929. }
  930. while ( hr == GetLdapHResultIfFailed( LDAP_ALREADY_EXISTS ) );
  931. if ( FAILED(hr) )
  932. {
  933. LOG((MSP_ERROR, "CNDNCDirectory::TestACLSafety - "
  934. "test addition failed and not duplicate - exit 0x%08x", hr));
  935. delete pDN;
  936. return hr;
  937. }
  938. //
  939. // Now that we have the test object, try modifying it.
  940. //
  941. LOG((MSP_TRACE, "CNDNCDirectory::TestACLSafety - "
  942. "trying to modify test object..."));
  943. HRESULT hrModify = NDNCSetTTL( m_ldap, pDN, MINIMUM_TTL );
  944. //
  945. // Now delete it. We do this even if we already know that the ACL is bad
  946. // because the modify failed; we want to get rid of the object if
  947. // possible.
  948. //
  949. LOG((MSP_TRACE, "CNDNCDirectory::TestACLSafety - "
  950. "trying to delete test object..."));
  951. hr = GetLdapHResultIfFailed( DoLdapDelete(m_ldap, pDN) );
  952. delete pDN;
  953. //
  954. // Now determine the verdict and return.
  955. //
  956. if ( FAILED(hr) )
  957. {
  958. LOG((MSP_ERROR, "CNDNCDirectory::TestACLSafety - "
  959. "test deletion (+ modification?) failed - ACL unsafe - "
  960. "exit 0x%08x", hr));
  961. return hr;
  962. }
  963. if ( FAILED( hrModify ) )
  964. {
  965. LOG((MSP_ERROR, "CNDNCDirectory::TestACLSafety - "
  966. "test deletion ok; test modification failed - ACL unsafe - "
  967. "exit 0x%08x", hrModify));
  968. return hrModify;
  969. }
  970. LOG((MSP_TRACE, "CNDNCDirectory::TestACLSafety - exit S_OK"));
  971. return S_OK;
  972. }
  973. HRESULT CNDNCDirectory::PublishRTPerson(
  974. IN TCHAR * pCN,
  975. IN TCHAR * pDN,
  976. IN TCHAR * pIPAddress,
  977. IN DWORD dwTTL,
  978. IN BOOL fModify,
  979. IN char * pSD,
  980. IN DWORD dwSDSize
  981. )
  982. /*++
  983. Routine Description:
  984. Create a RTPerson object in the dynamic container.
  985. Arguments:
  986. pCN - The cn of the user.
  987. pDN - The dn of the user.
  988. pIPAddress - The ip address of the machine.
  989. dwTTL - The ttl of this object.
  990. fModify - modify or add.
  991. Return Value:
  992. HRESULT.
  993. --*/
  994. {
  995. //
  996. // UPDATE THIS CONSTANT whenever you add an attribute below
  997. //
  998. static const DWORD DWATTRIBUTES = 4;
  999. // First create the object.
  1000. LDAPMod mod[DWATTRIBUTES];
  1001. DWORD dwCount = 0;
  1002. //
  1003. // We are not allowed to modify the object class. Therefore we only mention
  1004. // this if we are adding the object to the server, not modifying it.
  1005. //
  1006. // Fix: this is allocated on the stack, so we must do it here; if we stick
  1007. // it inside the if below, it gets deallocated immediately.
  1008. TCHAR * objectClass[] = {(WCHAR *)NDNC_RTPERSON, (WCHAR *)DYNAMICOBJECT, NULL};
  1009. if ( ! fModify )
  1010. {
  1011. // Object class.
  1012. // only need this attribute if not modifying.
  1013. mod[dwCount].mod_values = objectClass;
  1014. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  1015. mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
  1016. dwCount ++;
  1017. }
  1018. // msTAPI-uid
  1019. TCHAR* TAPIUid[] = {(WCHAR*)pCN, NULL};
  1020. mod[dwCount].mod_values = TAPIUid;
  1021. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  1022. mod[dwCount].mod_type = (WCHAR *)TAPIUID_NDNC;
  1023. dwCount ++;
  1024. // IP address.
  1025. TCHAR * IPPhone[] = {(WCHAR *)pIPAddress, NULL};
  1026. mod[dwCount].mod_values = IPPhone;
  1027. mod[dwCount].mod_op = LDAP_MOD_REPLACE;
  1028. mod[dwCount].mod_type = (WCHAR *)IPADDRESS_NDNC;
  1029. dwCount ++;
  1030. // these locals should not be within the "if" below... if
  1031. // they are, they might be deallocated before the function returns.
  1032. berval BerVal;
  1033. berval *sd[] = {&BerVal, NULL};
  1034. // The security descriptor attribute.
  1035. if ((char*)pSD != NULL)
  1036. {
  1037. BerVal.bv_len = dwSDSize;
  1038. BerVal.bv_val = (char*)pSD;
  1039. mod[dwCount].mod_bvalues = sd;
  1040. mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
  1041. mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
  1042. dwCount ++;
  1043. }
  1044. LDAPMod* mods[DWATTRIBUTES + 1];
  1045. DWORD i;
  1046. for (i = 0; i < dwCount; i ++)
  1047. {
  1048. mods[i] = &mod[i];
  1049. }
  1050. mods[i] = NULL;
  1051. if (fModify)
  1052. {
  1053. LOG((MSP_INFO, "modifying %S", pDN));
  1054. // Call the modify function to modify the object.
  1055. BAIL_IF_LDAP_FAIL(DoLdapModify(FALSE, m_ldap, pDN, mods), "modify RTPerson");
  1056. }
  1057. else
  1058. {
  1059. LOG((MSP_INFO, "adding %S", pDN));
  1060. // Call the add function to create the object.
  1061. BAIL_IF_LDAP_FAIL(DoLdapAdd(m_ldap, pDN, mods), "add RTPerson");
  1062. // next set the TTL value for this object
  1063. BAIL_IF_FAIL(NDNCSetTTL(m_ldap, pDN, (dwTTL == 0) ? m_TTL : dwTTL),
  1064. "Set ttl for RTPerson");
  1065. }
  1066. return S_OK;
  1067. }
  1068. HRESULT CNDNCDirectory::AddObjectToRefresh(
  1069. IN WCHAR *pDN,
  1070. IN long TTL
  1071. )
  1072. {
  1073. //
  1074. // Add a refresh table entry to the refresh table. The refresh table's add
  1075. // method does an element-by-element copy of the entry that it is given.
  1076. // This just copies the string pointer, so we need to allocate and copy
  1077. // the string here.
  1078. //
  1079. RefreshTableEntry entry;
  1080. entry.dwTTL = TTL;
  1081. entry.pDN = new WCHAR[ wcslen(pDN) + 1 ];
  1082. if ( entry.pDN == NULL )
  1083. {
  1084. LOG((MSP_ERROR, "Cannot allocate string for adding to refresh table"));
  1085. return E_OUTOFMEMORY;
  1086. }
  1087. wcscpy( entry.pDN, pDN );
  1088. //
  1089. // Now add it to the refresh table.
  1090. //
  1091. BOOL fSuccess = m_RefreshTable.add(entry);
  1092. if ( ! fSuccess )
  1093. {
  1094. LOG((MSP_ERROR, "Cannot add object to the refresh table"));
  1095. return E_OUTOFMEMORY;
  1096. }
  1097. return S_OK;
  1098. }
  1099. HRESULT CNDNCDirectory::RemoveObjectToRefresh(
  1100. IN WCHAR *pDN
  1101. )
  1102. {
  1103. //
  1104. // For each item in our refresh table
  1105. //
  1106. for ( DWORD i = 0; i < m_RefreshTable.size(); i++ )
  1107. {
  1108. //
  1109. // If the desired DN matches the one in this item
  1110. // then remove it and return success
  1111. //
  1112. if ( ! _wcsicmp( m_RefreshTable[i].pDN, pDN ) )
  1113. {
  1114. //
  1115. // We new'ed the string when we added the entry.
  1116. //
  1117. delete m_RefreshTable[i].pDN;
  1118. m_RefreshTable.removeAt(i);
  1119. return S_OK;
  1120. }
  1121. }
  1122. //
  1123. // If we get here then there was no matching item
  1124. //
  1125. LOG((MSP_ERROR, "Cannot remove object from the refresh table"));
  1126. return E_FAIL;
  1127. }
  1128. HRESULT CNDNCDirectory::AddUser(
  1129. IN ITDirectoryObject *pDirectoryObject,
  1130. IN BOOL fModify
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. Publish a new user object.
  1135. Arguments:
  1136. pDirectoryObject - The object to be published.
  1137. Return Value:
  1138. HRESULT.
  1139. --*/
  1140. {
  1141. // First find the interface for attributes.
  1142. CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
  1143. BAIL_IF_FAIL(
  1144. pDirectoryObject->QueryInterface(
  1145. IID_ITDirectoryObjectPrivate,
  1146. (void **)&pObjectPrivate
  1147. ),
  1148. "can't get the private directory object interface");
  1149. // Get the user name.
  1150. CBstr bName;
  1151. BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
  1152. "get user name");
  1153. // Get the user's machine name.
  1154. CBstr bIPPhone;
  1155. BAIL_IF_FAIL(pObjectPrivate->GetAttribute(UA_IPPHONE_PRIMARY, &bIPPhone),
  1156. "get IPPhone");
  1157. // Resolve the machine name and construct the CN for the user.
  1158. CTstr pCN;
  1159. DWORD dwIP;
  1160. BAIL_IF_FAIL(
  1161. MakeUserCN(bName, bIPPhone, &pCN, &dwIP),
  1162. "construct CN for user"
  1163. );
  1164. // Construct the DN for the RTPerson object.
  1165. CTstr pDNRTPerson;
  1166. BAIL_IF_FAIL(
  1167. MakeUserDN(pCN, dwIP, &pDNRTPerson),
  1168. "construct DN for user"
  1169. );
  1170. // convert the IP address to a string.
  1171. // This convertion is because of NetMeeting.
  1172. TCHAR IPAddress[80];
  1173. wsprintf(IPAddress, _T("%u"), dwIP);
  1174. DWORD dwTTL;
  1175. BAIL_IF_FAIL(pObjectPrivate->GetTTL(&dwTTL), "get User TTL");
  1176. // Get the Security Descriptor. The pointer pSD is just a copy of a pointer
  1177. // in the Conference object; the conference object retains ownership of the
  1178. // data and we must be careful not to delete or modify this data.
  1179. char * pSD;
  1180. DWORD dwSDSize;
  1181. BAIL_IF_FAIL(pObjectPrivate->GetConvertedSecurityDescriptor(&pSD, &dwSDSize),
  1182. "get user object security descriptor");
  1183. VARIANT_BOOL fChanged;
  1184. if ( SUCCEEDED( pObjectPrivate->
  1185. get_SecurityDescriptorIsModified( &fChanged ) ) )
  1186. {
  1187. if ( fChanged == VARIANT_FALSE )
  1188. {
  1189. pSD = NULL; // do NOT delete the string (see above)
  1190. dwSDSize = 0;
  1191. }
  1192. }
  1193. //
  1194. // NDNCs, unlike ILS, should return LDAP_ALREADY_EXISTS if the user object
  1195. // already exists. In ILS, this bug was a special hack for NetMeeting, but
  1196. // NetMeeting is no longer a factor with an NDNC.
  1197. //
  1198. HRESULT hr = PublishRTPerson(pCN, pDNRTPerson, IPAddress, dwTTL, fModify, pSD, dwSDSize);
  1199. if (FAILED(hr))
  1200. {
  1201. LOG((MSP_ERROR, "Can't publish a RTPerson, hr:%x", hr));
  1202. return hr;
  1203. }
  1204. //
  1205. // Add the RTPerson to the refresh list.
  1206. //
  1207. if (m_fAutoRefresh)
  1208. {
  1209. AddObjectToRefresh(pDNRTPerson, m_TTL);
  1210. }
  1211. return S_OK;
  1212. }
  1213. HRESULT CNDNCDirectory::DeleteConference(
  1214. IN ITDirectoryObject *pDirectoryObject
  1215. )
  1216. /*++
  1217. Routine Description:
  1218. Delete a conference from the NDNC server.
  1219. Arguments:
  1220. pDirectoryObject - The object to be deleted.
  1221. Return Value:
  1222. HRESULT.
  1223. --*/
  1224. {
  1225. // Get the name.
  1226. CBstr bName;
  1227. BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
  1228. "get conference name");
  1229. // construct the DN
  1230. CTstr pDN;
  1231. BAIL_IF_FAIL(
  1232. MakeConferenceDN(bName, &pDN), "construct DN for conference"
  1233. );
  1234. LOG((MSP_INFO, "deleting %S", pDN));
  1235. // Call the add function to create the object.
  1236. BAIL_IF_LDAP_FAIL(DoLdapDelete(m_ldap, pDN), "delete conference");
  1237. return S_OK;
  1238. }
  1239. HRESULT CNDNCDirectory::DeleteUser(
  1240. IN ITDirectoryObject *pDirectoryObject
  1241. )
  1242. /*++
  1243. Routine Description:
  1244. Delete a user from the NDNC server.
  1245. Arguments:
  1246. pDirectoryObject - The object to be deleted.
  1247. Return Value:
  1248. HRESULT.
  1249. --*/
  1250. {
  1251. // First find the interface for attributes.
  1252. CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
  1253. BAIL_IF_FAIL(
  1254. pDirectoryObject->QueryInterface(
  1255. IID_ITDirectoryObjectPrivate,
  1256. (void **)&pObjectPrivate
  1257. ),
  1258. "can't get the private directory object interface");
  1259. // Get the user name.
  1260. CBstr bName;
  1261. BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
  1262. "get user name");
  1263. // Get the user's machine name.
  1264. CBstr bIPPhone;
  1265. BAIL_IF_FAIL(pObjectPrivate->GetAttribute(UA_IPPHONE_PRIMARY, &bIPPhone),
  1266. "get IPPhone");
  1267. // Resolve the machine name and construct the CN for the user.
  1268. CTstr pCN;
  1269. DWORD dwIP;
  1270. BAIL_IF_FAIL(
  1271. MakeUserCN(bName, bIPPhone, &pCN, &dwIP),
  1272. "construct CN for user"
  1273. );
  1274. // Construct the DN for the RTPerson object.
  1275. CTstr pDNRTPerson;
  1276. BAIL_IF_FAIL(
  1277. MakeUserDN(pCN, dwIP, &pDNRTPerson),
  1278. "construct DN for user"
  1279. );
  1280. //
  1281. // Now delete the object from the server.
  1282. //
  1283. HRESULT hrFinal = S_OK;
  1284. HRESULT hr;
  1285. ULONG ulResult;
  1286. // Call the delete function to delete the RTPerson object, but keep
  1287. // going if it fails, noting the error code.
  1288. LOG((MSP_INFO, "deleting %S", pDNRTPerson));
  1289. ulResult = DoLdapDelete(m_ldap, pDNRTPerson);
  1290. hr = LogAndGetLdapHResult(ulResult, _T("delete RTPerson"));
  1291. if (FAILED(hr)) { hrFinal = hr; }
  1292. // we always remove the refresh objects, even if removal failed.
  1293. if (m_fAutoRefresh)
  1294. {
  1295. RemoveObjectToRefresh(pDNRTPerson);
  1296. }
  1297. return hrFinal;
  1298. }
  1299. HRESULT CNDNCDirectory::RefreshUser(
  1300. IN ITDirectoryObject *pDirectoryObject
  1301. )
  1302. /*++
  1303. Routine Description:
  1304. Refresh a user's TTL on the NDNC server.
  1305. Arguments:
  1306. pDirectoryObject - The object to be refreshed.
  1307. Return Value:
  1308. HRESULT.
  1309. --*/
  1310. {
  1311. // First find the interface for attributes.
  1312. CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
  1313. BAIL_IF_FAIL(
  1314. pDirectoryObject->QueryInterface(
  1315. IID_ITDirectoryObjectPrivate,
  1316. (void **)&pObjectPrivate
  1317. ),
  1318. "can't get the private directory object interface");
  1319. // Get the user name.
  1320. CBstr bName;
  1321. BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
  1322. "get user name");
  1323. // Get the user's machine name.
  1324. CBstr bIPPhone;
  1325. BAIL_IF_FAIL(pObjectPrivate->GetAttribute(UA_IPPHONE_PRIMARY, &bIPPhone),
  1326. "get IPPhone");
  1327. // Resolve the machine name and construct the CN for the user.
  1328. CTstr pCN;
  1329. DWORD dwIP;
  1330. BAIL_IF_FAIL(
  1331. MakeUserCN(bName, bIPPhone, &pCN, &dwIP),
  1332. "construct CN for user"
  1333. );
  1334. // Construct the DN for the RTPerson object.
  1335. CTstr pDNRTPerson;
  1336. BAIL_IF_FAIL(
  1337. MakeUserDN(pCN, dwIP, &pDNRTPerson),
  1338. "construct DN for user"
  1339. );
  1340. // set ttl for the RTPerson object.
  1341. BAIL_IF_LDAP_FAIL(NDNCSetTTL(m_ldap, pDNRTPerson, m_TTL), "set ttl for RTPerson");
  1342. //
  1343. // If the app has enabled auto-refresh but does not add or modify its
  1344. // user object, but rather, just refreshes it (because the object is still around
  1345. // from a previous instance of the app, and "add" would fail), we still need to
  1346. // autorefresh it, as that's what the app wants.
  1347. //
  1348. if (m_fAutoRefresh)
  1349. {
  1350. AddObjectToRefresh(pDNRTPerson, m_TTL);
  1351. }
  1352. return S_OK;
  1353. }
  1354. HRESULT CNDNCDirectory::CreateConference(
  1355. IN LDAPMessage * pEntry,
  1356. OUT ITDirectoryObject ** ppObject
  1357. )
  1358. /*++
  1359. Routine Description:
  1360. Create a conference object from the result of the LDAP search.
  1361. Arguments:
  1362. pEntry - The search result.
  1363. ppObject - The object to be created.
  1364. Return Value:
  1365. HRESULT.
  1366. --*/
  1367. {
  1368. CBstr bName, bProtocol, bBlob;
  1369. // Get the name of the conference.
  1370. BAIL_IF_FAIL(
  1371. ::GetAttributeValue(
  1372. m_ldap,
  1373. pEntry,
  1374. RTConferenceAttributeName(MA_MEETINGNAME),
  1375. &bName
  1376. ),
  1377. "get the conference name"
  1378. );
  1379. // Get the protocol ID of the conference.
  1380. BAIL_IF_FAIL(
  1381. ::GetAttributeValue(
  1382. m_ldap,
  1383. pEntry,
  1384. RTConferenceAttributeName(MA_PROTOCOL),
  1385. &bProtocol
  1386. ),
  1387. "get the conference protocol"
  1388. );
  1389. // Get the conference blob of the conference.
  1390. BAIL_IF_FAIL(
  1391. ::GetAttributeValue(
  1392. m_ldap,
  1393. pEntry,
  1394. RTConferenceAttributeName(MA_CONFERENCE_BLOB),
  1395. &bBlob
  1396. ),
  1397. "get the conference blob"
  1398. );
  1399. char * pSD = NULL;
  1400. DWORD dwSDSize = 0;
  1401. ::GetAttributeValueBer(
  1402. m_ldap,
  1403. pEntry,
  1404. NT_SECURITY_DESCRIPTOR,
  1405. &pSD,
  1406. &dwSDSize
  1407. );
  1408. // Create an empty conference.
  1409. HRESULT hr = ::CreateConferenceWithBlob(bName,
  1410. bProtocol,
  1411. bBlob,
  1412. pSD,
  1413. dwSDSize,
  1414. ppObject);
  1415. if ( FAILED(hr) )
  1416. {
  1417. LOG((MSP_ERROR, "CNDNCDirectory::CreateConference - "
  1418. "CreateConferenceWithBlob failed 0x%08x", hr));
  1419. delete pSD;
  1420. return hr;
  1421. }
  1422. //
  1423. // If the above succeeded then the conference object has taken ownership
  1424. // of pSD.
  1425. //
  1426. return S_OK;
  1427. }
  1428. HRESULT CNDNCDirectory::CreateUser(
  1429. IN LDAPMessage * pEntry,
  1430. IN ITDirectoryObject ** ppObject
  1431. )
  1432. /*++
  1433. Routine Description:
  1434. Create a user object from the result of the LDAP search.
  1435. Arguments:
  1436. pEntry - The search result.
  1437. ppObject - The object to be created.
  1438. Return Value:
  1439. HRESULT.
  1440. --*/
  1441. {
  1442. CBstr bName;
  1443. // Get the name of the user.
  1444. BAIL_IF_FAIL(
  1445. ::GetAttributeValue(
  1446. m_ldap,
  1447. pEntry,
  1448. RTPersonAttributeName(UA_TAPIUID),
  1449. &bName
  1450. ),
  1451. "get the user name"
  1452. );
  1453. CBstr bAddress;
  1454. // Grab the machine name from the name of the object. This can fail if
  1455. // we didn't publish the object (ie, it's a NetMeeting object). Also,
  1456. // check if the hostname isn't resolvable. In that case we also have
  1457. // to fall back on the IP address in the ipAddress attribute.
  1458. HRESULT hr;
  1459. hr = ParseUserName(bName, &bAddress);
  1460. if ( SUCCEEDED(hr) )
  1461. {
  1462. // Make sure we can get an IP from this name, at least for the moment.
  1463. // If not, release the name and indicate failure so we do our backup
  1464. // plan.
  1465. hr = ResolveHostName(0, bAddress, NULL, NULL);
  1466. if ( FAILED(hr) )
  1467. {
  1468. SysFreeString(bAddress);
  1469. }
  1470. }
  1471. if ( FAILED(hr) )
  1472. {
  1473. // In order to compatible with NetMeeting, we have to use IP address field.
  1474. CBstr bUglyIP;
  1475. BAIL_IF_FAIL(
  1476. ::GetAttributeValue(
  1477. m_ldap,
  1478. pEntry,
  1479. RTPersonAttributeName(UA_IPPHONE_PRIMARY),
  1480. &bUglyIP
  1481. ),
  1482. "get the user's IP address"
  1483. );
  1484. // We have to use NM's ugly format for the IP address. The IP address
  1485. // we got from netmeeting is a decimal string whose value is the dword
  1486. // value of the IP address in network order.
  1487. BAIL_IF_FAIL(UglyIPtoIP(bUglyIP, &bAddress), "Convert IP address");
  1488. }
  1489. // Create an empty user object.
  1490. CComPtr<ITDirectoryObject> pObject;
  1491. BAIL_IF_FAIL(::CreateEmptyUser(bName, &pObject), "CreateEmptyUser");
  1492. CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
  1493. BAIL_IF_FAIL(
  1494. pObject->QueryInterface(
  1495. IID_ITDirectoryObjectPrivate,
  1496. (void **)&pObjectPrivate
  1497. ),
  1498. "can't get the private directory object interface");
  1499. // Set the user attributes.
  1500. BAIL_IF_FAIL(pObjectPrivate->SetAttribute(UA_IPPHONE_PRIMARY, bAddress),
  1501. "set ipAddress");
  1502. //
  1503. // Set the security descriptor on the object.
  1504. //
  1505. char * pSD = NULL;
  1506. DWORD dwSDSize = 0;
  1507. ::GetAttributeValueBer(
  1508. m_ldap,
  1509. pEntry,
  1510. NT_SECURITY_DESCRIPTOR,
  1511. &pSD,
  1512. &dwSDSize
  1513. );
  1514. if ( pSD != NULL )
  1515. {
  1516. //
  1517. // Set the security descriptor in its "converted" (server) form.
  1518. //
  1519. hr = pObjectPrivate->PutConvertedSecurityDescriptor(pSD,
  1520. dwSDSize);
  1521. if ( FAILED(hr) )
  1522. {
  1523. LOG((MSP_ERROR, "PutConvertedSecurityDescriptor failed: %x", hr));
  1524. return hr;
  1525. }
  1526. }
  1527. *ppObject = pObject;
  1528. (*ppObject)->AddRef();
  1529. return S_OK;
  1530. }
  1531. HRESULT CNDNCDirectory::SearchObjects(
  1532. IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
  1533. IN BSTR pName,
  1534. OUT ITDirectoryObject *** pppDirectoryObject,
  1535. OUT DWORD * pdwSize
  1536. )
  1537. /*++
  1538. Routine Description:
  1539. Search the NDNC server for given type of objects.
  1540. Arguments:
  1541. DirectoryObjectType - The type of the object.
  1542. pName - The name to search for.
  1543. pppDirectoryObject - The returned array of objects.
  1544. pdwSize - The size of the array.
  1545. Return Value:
  1546. HRESULT.
  1547. --*/
  1548. {
  1549. TCHAR *pRDN;
  1550. TCHAR *pObjectClass;
  1551. TCHAR *Attributes[NUM_MEETING_ATTRIBUTES + 1]; // This is big enough now.
  1552. //
  1553. // Fill the attributes want to be returned, and construct the
  1554. // filter that we need.
  1555. //
  1556. switch (DirectoryObjectType)
  1557. {
  1558. case OT_CONFERENCE:
  1559. pRDN = (WCHAR *)UIDEQUALS_NDNC;
  1560. pObjectClass = (WCHAR *)NDNC_RTCONFERENCE;
  1561. Attributes[0] = (WCHAR *)RTConferenceAttributeName(MA_MEETINGNAME);
  1562. Attributes[1] = (WCHAR *)RTConferenceAttributeName(MA_PROTOCOL);
  1563. Attributes[2] = (WCHAR *)RTConferenceAttributeName(MA_CONFERENCE_BLOB);
  1564. Attributes[3] = (WCHAR *)NT_SECURITY_DESCRIPTOR;
  1565. Attributes[4] = NULL;
  1566. break;
  1567. case OT_USER:
  1568. pRDN = (WCHAR *)CNEQUALS;
  1569. pObjectClass = (WCHAR *)NDNC_RTPERSON;
  1570. Attributes[0] = (WCHAR *)RTPersonAttributeName(UA_USERNAME);
  1571. Attributes[1] = (WCHAR *)RTPersonAttributeName(UA_IPPHONE_PRIMARY);
  1572. Attributes[2] = (WCHAR *)RTPersonAttributeName(UA_TAPIUID);
  1573. Attributes[3] = (WCHAR *)NT_SECURITY_DESCRIPTOR;
  1574. Attributes[4] = NULL;
  1575. break;
  1576. default:
  1577. return E_FAIL;
  1578. }
  1579. //
  1580. // Construct the filter of the search.
  1581. // Finished strings should look like this:
  1582. // (&(entryTTL>=1)(objectCategory=msTAPI-RtConference)(uid=Name))
  1583. // (&(entryTTL>=1)(objectCategory=msTAPI-RtPerson)(cn=Name))
  1584. // We put out entryTTL, so from:
  1585. // (&(entryTTL>=1)(objectCategory=%s)(%s%s%s)) became
  1586. // (&(objectCategory=%s)(%s%s%s))
  1587. //
  1588. TCHAR NDNC_SEARCH_FILTER_FORMAT[] =
  1589. _T("(&(objectCategory=%s)(%s%s%s))");
  1590. CTstr pFilter = new TCHAR [lstrlen(NDNC_SEARCH_FILTER_FORMAT) +
  1591. lstrlen(pObjectClass) +
  1592. lstrlen(pRDN) +
  1593. lstrlen(pName) + 1];
  1594. BAIL_IF_NULL((TCHAR*)pFilter, E_OUTOFMEMORY);
  1595. TCHAR * pStar;
  1596. if (pName[lstrlen(pName) - 1] != _T('*'))
  1597. {
  1598. pStar = _T("*");
  1599. }
  1600. else
  1601. {
  1602. pStar = _T("");
  1603. }
  1604. wsprintf(pFilter, NDNC_SEARCH_FILTER_FORMAT,
  1605. pObjectClass, pRDN, pName, pStar);
  1606. // Search them.
  1607. CLdapMsgPtr pLdapMsg; // auto release message.
  1608. ULONG res = DoLdapSearch(
  1609. m_ldap, // ldap handle
  1610. m_pContainer, // schema dn
  1611. LDAP_SCOPE_ONELEVEL, // one level search
  1612. pFilter, // filter -- see above
  1613. Attributes, // array of attribute names
  1614. FALSE, // return the attribute values
  1615. &pLdapMsg, // search results
  1616. FALSE
  1617. );
  1618. BAIL_IF_LDAP_FAIL(res, "search for objects");
  1619. // count the returned entries.
  1620. DWORD dwEntries = ldap_count_entries(m_ldap, pLdapMsg);
  1621. ITDirectoryObject ** pObjects = new PDIRECTORYOBJECT [dwEntries];
  1622. BAIL_IF_NULL(pObjects, E_OUTOFMEMORY);
  1623. // Create objects.
  1624. DWORD dwCount = 0;
  1625. LDAPMessage *pEntry = ldap_first_entry(m_ldap, pLdapMsg);
  1626. while (pEntry != NULL)
  1627. {
  1628. HRESULT hr;
  1629. switch (DirectoryObjectType)
  1630. {
  1631. case OT_CONFERENCE:
  1632. hr = CreateConference(pEntry, &pObjects[dwCount]);
  1633. break;
  1634. case OT_USER:
  1635. hr = CreateUser(pEntry, &pObjects[dwCount]);
  1636. break;
  1637. }
  1638. if (SUCCEEDED(hr))
  1639. {
  1640. dwCount ++;
  1641. }
  1642. // Get next entry.
  1643. pEntry = ldap_next_entry(m_ldap, pEntry);
  1644. }
  1645. *pppDirectoryObject = pObjects;
  1646. *pdwSize = dwCount;
  1647. return S_OK;
  1648. }
  1649. /////////////////////////////////////////////////////////////////////////////
  1650. // NDNC Directory implementation
  1651. /////////////////////////////////////////////////////////////////////////////
  1652. STDMETHODIMP CNDNCDirectory::get_DirectoryType (
  1653. OUT DIRECTORY_TYPE * pDirectoryType
  1654. )
  1655. // Get the type of the directory.
  1656. {
  1657. if ( IsBadWritePtr(pDirectoryType, sizeof(DIRECTORY_TYPE) ) )
  1658. {
  1659. LOG((MSP_ERROR, "Directory.GetType, invalid pointer"));
  1660. return E_POINTER;
  1661. }
  1662. CLock Lock(m_lock);
  1663. *pDirectoryType = m_Type;
  1664. return S_OK;
  1665. }
  1666. STDMETHODIMP CNDNCDirectory::get_DisplayName (
  1667. OUT BSTR *ppServerName
  1668. )
  1669. // Get the display name of the directory.
  1670. {
  1671. BAIL_IF_BAD_WRITE_PTR(ppServerName, E_POINTER);
  1672. CLock Lock(m_lock);
  1673. //
  1674. // Rather m_pServerName, we'll use
  1675. // m_pServiceDnsName
  1676. //
  1677. if (m_pServiceDnsName == NULL)
  1678. {
  1679. *ppServerName = SysAllocString(L"");
  1680. }
  1681. else
  1682. {
  1683. *ppServerName = SysAllocString(m_pServiceDnsName);
  1684. }
  1685. if (*ppServerName == NULL)
  1686. {
  1687. LOG((MSP_ERROR, "get_DisplayName: out of memory."));
  1688. return E_OUTOFMEMORY;
  1689. }
  1690. return S_OK;
  1691. }
  1692. STDMETHODIMP CNDNCDirectory::get_IsDynamic(
  1693. OUT VARIANT_BOOL *pfDynamic
  1694. )
  1695. // find out if the directory is a dynamic one, meaning the object will be
  1696. // deleted after TTL runs out.
  1697. {
  1698. if ( IsBadWritePtr( pfDynamic, sizeof(VARIANT_BOOL) ) )
  1699. {
  1700. LOG((MSP_ERROR, "Directory.get_IsDynamic, invalid pointer"));
  1701. return E_POINTER;
  1702. }
  1703. *pfDynamic = VARIANT_TRUE;
  1704. return S_OK;
  1705. }
  1706. STDMETHODIMP CNDNCDirectory::get_DefaultObjectTTL(
  1707. OUT long *pTTL // in seconds
  1708. )
  1709. // The default TTL for object created. It is used when the object doesn't set
  1710. // a TTL. Conference object always has a TTL based on the stoptime.
  1711. {
  1712. if ( IsBadWritePtr( pTTL, sizeof(long) ) )
  1713. {
  1714. LOG((MSP_ERROR, "Directory.get_default objec TTL, invalid pointer"));
  1715. return E_POINTER;
  1716. }
  1717. CLock Lock(m_lock);
  1718. *pTTL = m_TTL;
  1719. return S_OK;
  1720. }
  1721. STDMETHODIMP CNDNCDirectory::put_DefaultObjectTTL(
  1722. IN long TTL // in sechods
  1723. )
  1724. // Change the default TTL, must be bigger then five minutes.
  1725. {
  1726. CLock Lock(m_lock);
  1727. if (TTL < MINIMUM_TTL)
  1728. {
  1729. return E_INVALIDARG;
  1730. }
  1731. m_TTL = TTL;
  1732. return S_OK;
  1733. }
  1734. STDMETHODIMP CNDNCDirectory::EnableAutoRefresh(
  1735. IN VARIANT_BOOL fEnable
  1736. )
  1737. // Enable auto refresh. Add this directory to the work threads that
  1738. // will notify the directory to update its objects.
  1739. {
  1740. HRESULT hr;
  1741. // ZoltanS: either VARIANT_TRUE or TRUE will work
  1742. // in case the caller doesn't know better
  1743. if (fEnable)
  1744. {
  1745. // Add this directory to the notify list of the work thread.
  1746. if (FAILED(hr = g_RendThread.AddDirectory(this)))
  1747. {
  1748. LOG((MSP_ERROR,
  1749. "Can not add this directory to the thread, %x", hr));
  1750. return hr;
  1751. }
  1752. }
  1753. else
  1754. {
  1755. // Remove this directory from the notify list of the work thread.
  1756. if (FAILED(hr = g_RendThread.RemoveDirectory(this)))
  1757. {
  1758. LOG((MSP_ERROR,
  1759. "Can not remove this directory from the thread, %x", hr));
  1760. return hr;
  1761. }
  1762. }
  1763. // ZoltanS: either VARIANT_TRUE or TRUE will work
  1764. // in case the caller doesn't know better
  1765. m_lock.Lock();
  1766. m_fAutoRefresh = ( fEnable ? VARIANT_TRUE : VARIANT_FALSE );
  1767. m_lock.Unlock();
  1768. return S_OK;
  1769. }
  1770. STDMETHODIMP CNDNCDirectory::Connect(
  1771. IN VARIANT_BOOL fSecure
  1772. )
  1773. // Connect to the server, using secure port or normal port.
  1774. {
  1775. LOG((MSP_TRACE, "CNDNCDirectory::Connect - enter"));
  1776. CLock Lock(m_lock);
  1777. if ( m_ldap != NULL )
  1778. {
  1779. LOG((MSP_ERROR, "already connected."));
  1780. return RND_ALREADY_CONNECTED;
  1781. }
  1782. if ( m_pServerName == NULL )
  1783. {
  1784. LOG((MSP_ERROR, "No server specified."));
  1785. return RND_NULL_SERVER_NAME;
  1786. }
  1787. if ( m_pServiceDnsName == NULL )
  1788. {
  1789. LOG((MSP_ERROR, "No serviceDnsName available; "
  1790. "Init should have failed and this object "
  1791. "should not exist."));
  1792. return E_UNEXPECTED;
  1793. }
  1794. // ZoltanS: either VARIANT_TRUE or TRUE will work
  1795. // in case the caller doesn't know better
  1796. if (fSecure)
  1797. {
  1798. // the port is flipped from regular port to ssl port.
  1799. m_wPort = GetOtherPort(m_wPort);
  1800. m_IsSsl = TRUE;
  1801. }
  1802. HRESULT hr = TryServer( m_wPort, m_pServiceDnsName );
  1803. if ( FAILED(hr) )
  1804. {
  1805. if( fSecure == VARIANT_TRUE )
  1806. {
  1807. hr = E_INVALIDARG;
  1808. }
  1809. LOG((MSP_ERROR, "CNDNCDirectory::Connect - "
  1810. "TryServer failed - exit 0x%08x", hr));
  1811. return hr;
  1812. }
  1813. //
  1814. // find out which interface we use to reach this server, to make sure
  1815. // we use that interface whenever we publish our own IP address
  1816. // If this fails we will not be able to publish anything, so fail!
  1817. //
  1818. hr = DiscoverInterface();
  1819. if ( FAILED(hr) )
  1820. {
  1821. if( m_ldap)
  1822. {
  1823. ldap_unbind(m_ldap);
  1824. m_ldap = NULL;
  1825. }
  1826. if( fSecure == VARIANT_TRUE )
  1827. {
  1828. hr = E_INVALIDARG;
  1829. }
  1830. LOG((MSP_ERROR, "CNDNCDirectory::Connect - "
  1831. "DiscoverInterface failed - exit 0x%08x", hr));
  1832. return hr;
  1833. }
  1834. LOG((MSP_TRACE, "CNDNCDirectory::Connect - exit S_OK"));
  1835. return S_OK;
  1836. }
  1837. HRESULT CNDNCDirectory::DiscoverInterface(void)
  1838. {
  1839. LOG((MSP_INFO, "CNDNCDirectory::DiscoverInterface - enter"));
  1840. //
  1841. // Winsock must be initialized at this point.
  1842. //
  1843. //
  1844. // Get the IP address of the server we're using
  1845. //
  1846. DWORD dwIP; // The IP address of the destination ILS server
  1847. HRESULT hr = ResolveHostName(0, m_pServerName, NULL, &dwIP);
  1848. if ( FAILED(hr) )
  1849. {
  1850. LOG((MSP_ERROR, "CNDNCDirectory::DiscoverInterface - "
  1851. "can't resolve host name - "
  1852. "strange, because we could connect! - exit 0x%08x", hr));
  1853. return hr;
  1854. }
  1855. //
  1856. // allocate a "fake" control socket
  1857. //
  1858. SOCKET hSocket = WSASocket(AF_INET, // af
  1859. SOCK_DGRAM, // type
  1860. IPPROTO_IP, // protocol
  1861. NULL, // lpProtocolInfo
  1862. 0, // g
  1863. 0 // dwFlags
  1864. );
  1865. if ( hSocket == INVALID_SOCKET )
  1866. {
  1867. hr = HRESULT_FROM_ERROR_CODE(WSAGetLastError());
  1868. LOG((MSP_ERROR, "CNDNCDirectory::DiscoverInterface - "
  1869. "WSASocket gave an invalid socket - exit 0x%08x", hr));
  1870. return hr;
  1871. }
  1872. //
  1873. // Query for the interface address based on destination.
  1874. //
  1875. SOCKADDR_IN DestAddr;
  1876. DestAddr.sin_family = AF_INET;
  1877. DestAddr.sin_port = 0;
  1878. DestAddr.sin_addr.s_addr = dwIP;
  1879. SOCKADDR_IN LocAddr;
  1880. DWORD dwStatus;
  1881. DWORD dwLocAddrSize = sizeof(SOCKADDR_IN);
  1882. DWORD dwNumBytesReturned = 0;
  1883. dwStatus = WSAIoctl(
  1884. hSocket, // SOCKET s
  1885. SIO_ROUTING_INTERFACE_QUERY, // DWORD dwIoControlCode
  1886. &DestAddr, // LPVOID lpvInBuffer
  1887. sizeof(SOCKADDR_IN), // DWORD cbInBuffer
  1888. &LocAddr, // LPVOID lpvOUTBuffer
  1889. dwLocAddrSize, // DWORD cbOUTBuffer
  1890. &dwNumBytesReturned, // LPDWORD lpcbBytesReturned
  1891. NULL, // LPWSAOVERLAPPED lpOverlapped
  1892. NULL // LPWSAOVERLAPPED_COMPLETION_ROUTINE lpComplROUTINE
  1893. );
  1894. //
  1895. // Don't close the socket yet, because the closesocket() call will
  1896. // overwrite the WSAGetLastError value in the failure case!
  1897. //
  1898. // Check for error and then close the socket.
  1899. //
  1900. if ( dwStatus == SOCKET_ERROR )
  1901. {
  1902. hr = HRESULT_FROM_ERROR_CODE(WSAGetLastError());
  1903. LOG((MSP_ERROR, "CNDNCDirectory::DiscoverInterface - "
  1904. "WSAIoctl failed - exit 0x%08x", hr));
  1905. closesocket(hSocket);
  1906. return hr;
  1907. }
  1908. closesocket(hSocket);
  1909. //
  1910. // Success - save the returned address in our member variable.
  1911. // Stored in network byte order.
  1912. //
  1913. m_dwInterfaceAddress = LocAddr.sin_addr.s_addr;
  1914. LOG((MSP_INFO, "CNDNCDirectory::DiscoverInterface - exit S_OK"));
  1915. return S_OK;
  1916. }
  1917. //
  1918. // ITDirectory::Bind
  1919. //
  1920. // Bind to the server.
  1921. //
  1922. // Currently recognized flags:
  1923. //
  1924. // RENDBIND_AUTHENTICATE 0x00000001
  1925. // RENDBIND_DEFAULTDOMAINNAME 0x00000002
  1926. // RENDBIND_DEFAULTUSERNAME 0x00000004
  1927. // RENDBIND_DEFAULTPASSWORD 0x00000008
  1928. //
  1929. // "Meta-flags" for convenience:
  1930. // RENDBIND_DEFAULTCREDENTIALS 0x0000000e
  1931. //
  1932. //
  1933. // All of this together means that the following three
  1934. // forms are all equivalent:
  1935. //
  1936. // BSTR es = SysAllocString(L"");
  1937. // hr = pITDirectory->Bind(es, es, es, RENDBIND_AUTHENTICATE |
  1938. // RENDBIND_DEFAULTCREDENTIALS);
  1939. // SysFreeString(es);
  1940. //
  1941. //
  1942. // BSTR es = SysAllocString(L"");
  1943. // hr = pITDirectory->Bind(es, es, es, RENDBIND_AUTHENTICATE |
  1944. // RENDBIND_DEFAULTDOMAINNAME |
  1945. // RENDBIND_DEFAULTUSERNAME |
  1946. // RENDBIND_DEFAULTPASSWORD);
  1947. // SysFreeString(es);
  1948. //
  1949. //
  1950. // hr = pITDirectory->Bind(NULL, NULL, NULL, RENDBIND_AUTHENTICATE);
  1951. //
  1952. //
  1953. STDMETHODIMP CNDNCDirectory::Bind (
  1954. IN BSTR pDomainName,
  1955. IN BSTR pUserName,
  1956. IN BSTR pPassword,
  1957. IN long lFlags
  1958. )
  1959. {
  1960. LOG((MSP_TRACE, "CNDNCDirectory Bind - enter"));
  1961. //
  1962. // Determine if we should authenticate.
  1963. //
  1964. BOOL fAuthenticate = FALSE;
  1965. if ( lFlags & RENDBIND_AUTHENTICATE )
  1966. {
  1967. fAuthenticate = TRUE;
  1968. }
  1969. //
  1970. // For scripting compatibility, force string parameters to NULL based
  1971. // on flags.
  1972. //
  1973. if ( lFlags & RENDBIND_DEFAULTDOMAINNAME )
  1974. {
  1975. pDomainName = NULL;
  1976. }
  1977. if ( lFlags & RENDBIND_DEFAULTUSERNAME )
  1978. {
  1979. pUserName = NULL;
  1980. }
  1981. if ( lFlags & RENDBIND_DEFAULTPASSWORD )
  1982. {
  1983. pPassword = NULL;
  1984. }
  1985. LOG((MSP_INFO, "Bind parameters: domain: `%S' user: `%S' pass: `%S'"
  1986. "authenticate: %S)",
  1987. (pDomainName) ? pDomainName : L"<null>",
  1988. (pUserName) ? pUserName : L"<null>",
  1989. (pPassword) ? pPassword : L"<null>",
  1990. (fAuthenticate) ? L"yes" : L"no"));
  1991. //
  1992. // All flags processed -- lock and proceed with bind if connected.
  1993. //
  1994. CLock Lock(m_lock);
  1995. if (m_ldap == NULL)
  1996. {
  1997. LOG((MSP_ERROR, "not connected."));
  1998. return RND_NOT_CONNECTED;
  1999. }
  2000. //
  2001. // ZoltanS: check the arguments. NULL has meaning in each case, so they are
  2002. // OK for now. In each case we want to check any length string, so we
  2003. // specify (UINT) -1 as the length.
  2004. //
  2005. if ( (pDomainName != NULL) && IsBadStringPtr(pDomainName, (UINT) -1 ) )
  2006. {
  2007. LOG((MSP_ERROR, "CNDNCDirectory::Bind: bad non-NULL pDomainName argument"));
  2008. return E_POINTER;
  2009. }
  2010. if ( (pUserName != NULL) && IsBadStringPtr(pUserName, (UINT) -1 ) )
  2011. {
  2012. LOG((MSP_ERROR, "CNDNCDirectory::Bind: bad non-NULL pUserName argument"));
  2013. return E_POINTER;
  2014. }
  2015. if ( (pPassword != NULL) && IsBadStringPtr(pPassword, (UINT) -1 ) )
  2016. {
  2017. LOG((MSP_ERROR, "CNDNCDirectory::Bind: bad non-NULL pPassword argument"));
  2018. return E_POINTER;
  2019. }
  2020. ULONG res;
  2021. if ( m_IsSsl || (!fAuthenticate) )
  2022. {
  2023. // if encrypted or no secure authentication is required,
  2024. // simple bind is sufficient
  2025. // ldap_simple_bind_s does not use sspi to get default credentials. We are
  2026. // just specifying what we will actually pass on the wire.
  2027. if (pPassword == NULL)
  2028. {
  2029. LOG((MSP_ERROR, "invalid Bind parameters: no password specified"));
  2030. return E_INVALIDARG;
  2031. }
  2032. WCHAR * wszFullName;
  2033. if ( (pDomainName == NULL) && (pUserName == NULL) )
  2034. {
  2035. // No domain / user doesn't make sense.
  2036. LOG((MSP_ERROR, "invalid Bind paramters: domain and user not specified"));
  2037. return E_INVALIDARG;
  2038. }
  2039. else if (pDomainName == NULL)
  2040. {
  2041. // username only is okay
  2042. wszFullName = pUserName;
  2043. }
  2044. else if (pUserName == NULL)
  2045. {
  2046. // It doesn't make sense to specify domain but not user...
  2047. LOG((MSP_ERROR, "invalid Bind paramters: domain specified but not user"));
  2048. return E_INVALIDARG;
  2049. }
  2050. else
  2051. {
  2052. // We need domain\user. Allocate a string and sprintf into it.
  2053. // The + 2 is for the "\" and for the null termination.
  2054. wszFullName = new WCHAR[wcslen(pDomainName) + wcslen(pUserName) + 2];
  2055. BAIL_IF_NULL(wszFullName, E_OUTOFMEMORY);
  2056. wsprintf(wszFullName, L"%s\\%s", pDomainName, pUserName);
  2057. }
  2058. //
  2059. // Do the simple bind.
  2060. //
  2061. res = ldap_simple_bind_s(m_ldap, wszFullName, pPassword);
  2062. //
  2063. // If we constructed the full name string, we now need to delete it.
  2064. //
  2065. if (wszFullName != pUserName)
  2066. {
  2067. delete wszFullName;
  2068. }
  2069. //
  2070. // Bail if the simple bind failed.
  2071. //
  2072. BAIL_IF_LDAP_FAIL(res, "ldap simple bind");
  2073. }
  2074. else // try an SSPI bind
  2075. {
  2076. // ZoltanS Note: the ldap bind code does not process NULL, NULL, NULL
  2077. // in the SEC_WINNT_AUTH_IDENTITY blob, therefore it is special-cased.
  2078. // ZoltanS: We used to use LDAP_AUTH_NTLM; now we use
  2079. // LDAP_AUTH_NEGOTIATE to make sure we use the right domain for the
  2080. // bind.
  2081. if ( pDomainName || pUserName || pPassword )
  2082. {
  2083. // fill the credential structure
  2084. SEC_WINNT_AUTH_IDENTITY AuthI;
  2085. AuthI.User = (PTCHAR)pUserName;
  2086. AuthI.UserLength = (pUserName == NULL)? 0: wcslen(pUserName);
  2087. AuthI.Domain = (PTCHAR)pDomainName;
  2088. AuthI.DomainLength = (pDomainName == NULL)? 0: wcslen(pDomainName);
  2089. AuthI.Password = (PTCHAR)pPassword;
  2090. AuthI.PasswordLength = (pPassword == NULL)? 0: wcslen(pPassword);
  2091. AuthI.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  2092. res = ldap_bind_s(m_ldap, NULL, (TCHAR*)(&AuthI), LDAP_AUTH_NEGOTIATE);
  2093. BAIL_IF_LDAP_FAIL(res, "bind with authentication");
  2094. }
  2095. else
  2096. {
  2097. // Otherwise we've come in with NULL, NULL, NULL -
  2098. // pass in NULL, NULL. The reason do this is that ldap bind code
  2099. // does not process NULL, NULL, NULL in the
  2100. // SEC_WINNT_AUTH_IDENTITY blob !!!
  2101. ULONG res = ldap_bind_s(m_ldap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  2102. BAIL_IF_LDAP_FAIL(res, "bind with NULL NULL NULL");
  2103. }
  2104. }
  2105. LOG((MSP_TRACE, "CNDNCDirectory::Bind - exiting OK"));
  2106. return S_OK;
  2107. }
  2108. //////////////////////////////////////////////////////////////////////
  2109. // ITDirectory::AddDirectoryObject
  2110. //
  2111. // Return values:
  2112. // Value Where defined What it means
  2113. // ----- ------------- -------------
  2114. // RND_NOT_CONNECTED .\rnderr.h ::Connect not yet called
  2115. // E_POINTER sdk\inc\winerror.h pDirectoryObject is a bad pointer
  2116. // other from AddConference
  2117. // other from AddUser
  2118. //
  2119. STDMETHODIMP CNDNCDirectory::AddDirectoryObject (
  2120. IN ITDirectoryObject *pDirectoryObject
  2121. )
  2122. // publish a new object to the server.
  2123. {
  2124. if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
  2125. {
  2126. LOG((MSP_ERROR, "CNDNCDirectory::AddDirectoryObject - "
  2127. "bad directory object pointer - returning E_POINTER"));
  2128. return E_POINTER;
  2129. }
  2130. CLock Lock(m_lock);
  2131. if (m_ldap == NULL)
  2132. {
  2133. LOG((MSP_ERROR, "CNDNCDirectory::AddDirectoryObject - not connected."));
  2134. return RND_NOT_CONNECTED;
  2135. }
  2136. HRESULT hr;
  2137. DIRECTORY_OBJECT_TYPE type;
  2138. hr = pDirectoryObject->get_ObjectType(&type);
  2139. if ( FAILED(hr) )
  2140. {
  2141. LOG((MSP_ERROR, "CNDNCDirectory::AddDirectoryObject - "
  2142. "can't get object type; returning 0x%08x", hr));
  2143. return hr;
  2144. }
  2145. switch (type)
  2146. {
  2147. case OT_CONFERENCE:
  2148. hr = AddConference(pDirectoryObject, FALSE);
  2149. break;
  2150. case OT_USER:
  2151. hr = AddUser(pDirectoryObject, FALSE);
  2152. break;
  2153. }
  2154. return hr;
  2155. }
  2156. STDMETHODIMP CNDNCDirectory::ModifyDirectoryObject (
  2157. IN ITDirectoryObject *pDirectoryObject
  2158. )
  2159. // modify an object on the server.
  2160. {
  2161. if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
  2162. {
  2163. LOG((MSP_ERROR, "CNDNCDirectory::ModifyDirectoryObject - "
  2164. "bad directory object pointer - returning E_POINTER"));
  2165. return E_POINTER;
  2166. }
  2167. CLock Lock(m_lock);
  2168. if (m_ldap == NULL)
  2169. {
  2170. LOG((MSP_ERROR, "CNDNCDirectory::ModifyDirectoryObject - not connected."));
  2171. return RND_NOT_CONNECTED;
  2172. }
  2173. HRESULT hr;
  2174. DIRECTORY_OBJECT_TYPE type;
  2175. hr = pDirectoryObject->get_ObjectType(&type);
  2176. if ( FAILED(hr) )
  2177. {
  2178. LOG((MSP_ERROR, "CNDNCDirectory::ModifyDirectoryObject - "
  2179. "can't get object type; returning 0x%08x", hr));
  2180. return hr;
  2181. }
  2182. switch (type)
  2183. {
  2184. case OT_CONFERENCE:
  2185. hr = AddConference(pDirectoryObject, TRUE);
  2186. break;
  2187. case OT_USER:
  2188. hr = AddUser(pDirectoryObject, TRUE);
  2189. break;
  2190. }
  2191. return hr;
  2192. }
  2193. STDMETHODIMP CNDNCDirectory::RefreshDirectoryObject (
  2194. IN ITDirectoryObject *pDirectoryObject
  2195. )
  2196. // Refresh the TTL for the object and add the object to the refresh list
  2197. // if the autorefresh is enabled.
  2198. {
  2199. if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
  2200. {
  2201. LOG((MSP_ERROR, "CNDNCDirectory::RefreshDirectoryObject - "
  2202. "bad directory object pointer - returning E_POINTER"));
  2203. return E_POINTER;
  2204. }
  2205. CLock Lock(m_lock);
  2206. if (m_ldap == NULL)
  2207. {
  2208. LOG((MSP_ERROR, "CNDNCDirectory::RefreshDirectoryObject - not connected."));
  2209. return RND_NOT_CONNECTED;
  2210. }
  2211. HRESULT hr;
  2212. DIRECTORY_OBJECT_TYPE type;
  2213. hr = pDirectoryObject->get_ObjectType(&type);
  2214. if ( FAILED(hr) )
  2215. {
  2216. LOG((MSP_ERROR, "CNDNCDirectory::RefreshDirectoryObject - "
  2217. "can't get object type; returning 0x%08x", hr));
  2218. return hr;
  2219. }
  2220. switch (type)
  2221. {
  2222. case OT_CONFERENCE:
  2223. return S_OK; // conferences do not need refresh.
  2224. case OT_USER:
  2225. hr = RefreshUser(pDirectoryObject);
  2226. break;
  2227. }
  2228. return hr;
  2229. }
  2230. STDMETHODIMP CNDNCDirectory::DeleteDirectoryObject (
  2231. IN ITDirectoryObject *pDirectoryObject
  2232. )
  2233. // delete an object on the server.
  2234. {
  2235. if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
  2236. {
  2237. LOG((MSP_ERROR, "CNDNCDirectory::DeleteDirectoryObject - "
  2238. "bad directory object pointer - returning E_POINTER"));
  2239. return E_POINTER;
  2240. }
  2241. CLock Lock(m_lock);
  2242. if (m_ldap == NULL)
  2243. {
  2244. LOG((MSP_ERROR, "CNDNCDirectory::DeleteDirectoryObject - not connected."));
  2245. return RND_NOT_CONNECTED;
  2246. }
  2247. HRESULT hr;
  2248. DIRECTORY_OBJECT_TYPE type;
  2249. hr = pDirectoryObject->get_ObjectType(&type);
  2250. if ( FAILED(hr) )
  2251. {
  2252. LOG((MSP_ERROR, "CNDNCDirectory::DeleteDirectoryObject - "
  2253. "can't get object type; returning 0x%08x", hr));
  2254. return hr;
  2255. }
  2256. switch (type)
  2257. {
  2258. case OT_CONFERENCE:
  2259. hr = DeleteConference(pDirectoryObject);
  2260. break;
  2261. case OT_USER:
  2262. hr = DeleteUser(pDirectoryObject);
  2263. break;
  2264. }
  2265. return hr;
  2266. }
  2267. STDMETHODIMP CNDNCDirectory::get_DirectoryObjects (
  2268. IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
  2269. IN BSTR pName,
  2270. OUT VARIANT * pVariant
  2271. )
  2272. // search for objects on the server.
  2273. {
  2274. BAIL_IF_BAD_READ_PTR(pName, E_POINTER);
  2275. BAIL_IF_BAD_WRITE_PTR(pVariant, E_POINTER);
  2276. CLock Lock(m_lock);
  2277. if (m_ldap == NULL)
  2278. {
  2279. LOG((MSP_ERROR, "not connected."));
  2280. return RND_NOT_CONNECTED;
  2281. }
  2282. HRESULT hr;
  2283. ITDirectoryObject **pObjects;
  2284. DWORD dwSize;
  2285. // search and create objects.
  2286. hr = SearchObjects(DirectoryObjectType, pName, &pObjects, &dwSize);
  2287. BAIL_IF_FAIL(hr, "Search for objects");
  2288. // create a collection object that contains the objects.
  2289. hr = CreateInterfaceCollection(dwSize, // count
  2290. &pObjects[0], // begin ptr
  2291. &pObjects[dwSize], // end ptr
  2292. pVariant); // return value
  2293. for (DWORD i = 0; i < dwSize; i ++)
  2294. {
  2295. pObjects[i]->Release();
  2296. }
  2297. delete pObjects;
  2298. BAIL_IF_FAIL(hr, "Create collection of directory objects");
  2299. return hr;
  2300. }
  2301. STDMETHODIMP CNDNCDirectory::EnumerateDirectoryObjects (
  2302. IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
  2303. IN BSTR pName,
  2304. OUT IEnumDirectoryObject ** ppEnumObject
  2305. )
  2306. // search for the objects on the server.
  2307. {
  2308. BAIL_IF_BAD_READ_PTR(pName, E_POINTER);
  2309. BAIL_IF_BAD_WRITE_PTR(ppEnumObject, E_POINTER);
  2310. CLock Lock(m_lock);
  2311. if (m_ldap == NULL)
  2312. {
  2313. LOG((MSP_ERROR, "not connected."));
  2314. return RND_NOT_CONNECTED;
  2315. }
  2316. HRESULT hr;
  2317. ITDirectoryObject **pObjects;
  2318. DWORD dwSize;
  2319. // search and create objects.
  2320. hr = SearchObjects(DirectoryObjectType, pName, &pObjects, &dwSize);
  2321. BAIL_IF_FAIL(hr, "Search for objects");
  2322. // create a enumerator object that contains the objects.
  2323. hr = ::CreateDirectoryObjectEnumerator(
  2324. &pObjects[0],
  2325. &pObjects[dwSize],
  2326. ppEnumObject
  2327. );
  2328. for (DWORD i = 0; i < dwSize; i ++)
  2329. {
  2330. pObjects[i]->Release();
  2331. }
  2332. delete pObjects;
  2333. BAIL_IF_FAIL(hr, "Create enumerator of directory objects");
  2334. return hr;
  2335. }
  2336. /////////////////////////////////////////////////////////////////////////////
  2337. // ILSConfig implementation
  2338. /////////////////////////////////////////////////////////////////////////////
  2339. STDMETHODIMP CNDNCDirectory::get_Port (
  2340. OUT long *pPort
  2341. )
  2342. // get the current port used in Ldap connection.
  2343. {
  2344. if ( IsBadWritePtr(pPort, sizeof(long) ) )
  2345. {
  2346. LOG((MSP_ERROR, "Directory.get_Port, invalid pointer"));
  2347. return E_POINTER;
  2348. }
  2349. CLock Lock(m_lock);
  2350. *pPort = (long)m_wPort;
  2351. return S_OK;
  2352. }
  2353. STDMETHODIMP CNDNCDirectory::put_Port (
  2354. IN long Port
  2355. )
  2356. // set the port the user wants to use.
  2357. {
  2358. CLock Lock(m_lock);
  2359. if (m_ldap != NULL)
  2360. {
  2361. LOG((MSP_ERROR, "already connected."));
  2362. return RND_ALREADY_CONNECTED;
  2363. }
  2364. if (Port <= USHRT_MAX)
  2365. {
  2366. m_wPort = (WORD)Port;
  2367. return S_OK;
  2368. }
  2369. return E_INVALIDARG;
  2370. }
  2371. /////////////////////////////////////////////////////////////////////////////
  2372. // ITDynamic interface
  2373. /////////////////////////////////////////////////////////////////////////////
  2374. STDMETHODIMP CNDNCDirectory::Update(DWORD dwSecondsPassed)
  2375. // Update the TTL for object created in this directory. The worker thread
  2376. // sends a tick every minute.
  2377. {
  2378. if ( ! m_lock.TryLock() )
  2379. {
  2380. return S_OK;
  2381. }
  2382. LOG((MSP_TRACE, "CNDNCDirectory::Update is called, delta: %d", dwSecondsPassed));
  2383. //
  2384. // Go through the table to see if anyone needs refresh.
  2385. //
  2386. for ( DWORD i = 0; i < m_RefreshTable.size(); i++ )
  2387. {
  2388. WCHAR * pDN = m_RefreshTable[i].pDN;
  2389. DWORD dwTTL = m_RefreshTable[i].dwTTL;
  2390. LOG((MSP_TRACE, "\tExamining user object: %S", pDN ));
  2391. LOG((MSP_TRACE, "\t\tTime remaining: %d", dwTTL ));
  2392. if ( dwTTL <= ( 2 * dwSecondsPassed ) )
  2393. {
  2394. //
  2395. // refresh it if the TTL is going to expire within the next
  2396. // two clicks
  2397. //
  2398. LOG((MSP_TRACE, "\t\t\tREFRESHING"));
  2399. if ( SUCCEEDED( NDNCSetTTL( m_ldap, pDN, m_TTL) ) )
  2400. {
  2401. m_RefreshTable[i].dwTTL = m_TTL;
  2402. }
  2403. else
  2404. {
  2405. LOG((MSP_WARN, "\t\t\t\tRefresh failed; will try again next time"));
  2406. }
  2407. }
  2408. else
  2409. {
  2410. //
  2411. // Not about to expire right now so just keep track of the time before
  2412. // it expires
  2413. //
  2414. LOG((MSP_TRACE, "\t\t\tdecrementing"));
  2415. m_RefreshTable[i].dwTTL -= dwSecondsPassed;
  2416. }
  2417. }
  2418. m_lock.Unlock();
  2419. LOG((MSP_TRACE, "CNDNCDirectory::Update exit S_OK"));
  2420. return S_OK;
  2421. }
  2422. typedef IDispatchImpl<ITNDNCDirectoryVtbl<CNDNCDirectory>, &IID_ITDirectory, &LIBID_RENDLib> CTNDNCDirectory;
  2423. typedef IDispatchImpl<ITNDNCILSConfigVtbl<CNDNCDirectory>, &IID_ITILSConfig, &LIBID_RENDLib> CTNDNCILSConfig;
  2424. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
  2425. //
  2426. // CNDNCDirectory::GetIDsOfNames
  2427. //
  2428. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
  2429. STDMETHODIMP CNDNCDirectory::GetIDsOfNames(REFIID riid,
  2430. LPOLESTR* rgszNames,
  2431. UINT cNames,
  2432. LCID lcid,
  2433. DISPID* rgdispid
  2434. )
  2435. {
  2436. LOG((MSP_TRACE, "CNDNCDirectory::GetIDsOfNames[%p] - enter. Name [%S]",this, *rgszNames));
  2437. HRESULT hr = DISP_E_UNKNOWNNAME;
  2438. //
  2439. // See if the requsted method belongs to the default interface
  2440. //
  2441. hr = CTNDNCDirectory::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  2442. if (SUCCEEDED(hr))
  2443. {
  2444. LOG((MSP_TRACE, "CNDNCDirectory::GetIDsOfNames - found %S on CTNDNCDirectory", *rgszNames));
  2445. rgdispid[0] |= IDISPDIRECTORY;
  2446. return hr;
  2447. }
  2448. //
  2449. // If not, then try the ITILSConfig base class
  2450. //
  2451. hr = CTNDNCILSConfig::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  2452. if (SUCCEEDED(hr))
  2453. {
  2454. LOG((MSP_TRACE, "CNDNCDirectory::GetIDsOfNames - found %S on CTNDNCILSConfig", *rgszNames));
  2455. rgdispid[0] |= IDISPILSCONFIG;
  2456. return hr;
  2457. }
  2458. LOG((MSP_ERROR, "CNDNCDirectory::GetIDsOfNames[%p] - finish. didn't find %S on our iterfaces",*rgszNames));
  2459. return hr;
  2460. }
  2461. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
  2462. //
  2463. // CNDNCDirectory::Invoke
  2464. //
  2465. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
  2466. STDMETHODIMP CNDNCDirectory::Invoke(DISPID dispidMember,
  2467. REFIID riid,
  2468. LCID lcid,
  2469. WORD wFlags,
  2470. DISPPARAMS* pdispparams,
  2471. VARIANT* pvarResult,
  2472. EXCEPINFO* pexcepinfo,
  2473. UINT* puArgErr
  2474. )
  2475. {
  2476. LOG((MSP_TRACE, "CNDNCDirectory::Invoke[%p] - enter. dispidMember %lx",this, dispidMember));
  2477. HRESULT hr = DISP_E_MEMBERNOTFOUND;
  2478. DWORD dwInterface = (dispidMember & INTERFACEMASK);
  2479. //
  2480. // Call invoke for the required interface
  2481. //
  2482. switch (dwInterface)
  2483. {
  2484. case IDISPDIRECTORY:
  2485. {
  2486. hr = CTNDNCDirectory::Invoke(dispidMember,
  2487. riid,
  2488. lcid,
  2489. wFlags,
  2490. pdispparams,
  2491. pvarResult,
  2492. pexcepinfo,
  2493. puArgErr
  2494. );
  2495. LOG((MSP_TRACE, "CNDNCDirectory::Invoke - ITDirectory"));
  2496. break;
  2497. }
  2498. case IDISPILSCONFIG:
  2499. {
  2500. hr = CTNDNCILSConfig::Invoke(dispidMember,
  2501. riid,
  2502. lcid,
  2503. wFlags,
  2504. pdispparams,
  2505. pvarResult,
  2506. pexcepinfo,
  2507. puArgErr
  2508. );
  2509. LOG((MSP_TRACE, "CNDNCDirectory::Invoke - ITILSConfig"));
  2510. break;
  2511. }
  2512. } // end switch (dwInterface)
  2513. LOG((MSP_TRACE, "CNDNCDirectory::Invoke[%p] - finish. hr = %lx", hr));
  2514. return hr;
  2515. }