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.

3050 lines
78 KiB

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