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

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