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.

864 lines
18 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: cggi.cxx
  7. //
  8. // Contents: This file contains the Group Object's
  9. // IADsGroup and IADsGroupOperation methods
  10. //
  11. // History: 11-1-95 krishnag Created.
  12. //
  13. //----------------------------------------------------------------------------
  14. #include "ldap.hxx"
  15. #pragma hdrstop
  16. #include <winldap.h>
  17. #include "..\ldapc\ldpcache.hxx"
  18. #include "..\ldapc\ldaputil.hxx"
  19. #include "..\ldapc\parse.hxx"
  20. HRESULT
  21. BuildLDAPPathFromADsPath2(
  22. LPWSTR szADsPathName,
  23. LPWSTR *pszLDAPServer,
  24. LPWSTR *pszLDAPDn,
  25. DWORD * pdwPort
  26. );
  27. HRESULT
  28. LdapGetSyntaxOfAttributeOnServer(
  29. LPTSTR pszServerPath,
  30. LPTSTR pszAttrName,
  31. DWORD *pdwSyntaxId,
  32. CCredentials& Credentials,
  33. DWORD dwPort,
  34. BOOL fFromServer = FALSE
  35. );
  36. HRESULT
  37. ReadServerSupportsIsADControl(
  38. LPWSTR pszLDAPServer,
  39. BOOL * pfDomScopeSupported,
  40. CCredentials& Credentials,
  41. DWORD dwPort
  42. );
  43. BOOL
  44. VerifyIfMember(
  45. BSTR bstrMember,
  46. VARIANT * VariantArray,
  47. ULONG cElementFetched
  48. );
  49. HRESULT
  50. ValidateProvider(
  51. POBJECTINFO pObjectInfo
  52. );
  53. BOOL
  54. MapLdapClassToADsClass(
  55. LPTSTR *aLdapClasses,
  56. int nCount,
  57. LPTSTR pszADsClass
  58. );
  59. struct _classmapping
  60. {
  61. LPTSTR pszLdapClassName;
  62. LPTSTR pszADsClassName;
  63. } aClassMap[] =
  64. {
  65. { TEXT("user"), USER_CLASS_NAME}, // NTDS
  66. { TEXT("group"), GROUP_CLASS_NAME},
  67. { TEXT("localGroup"), GROUP_CLASS_NAME},
  68. { TEXT("printQueue"), PRINTER_CLASS_NAME},
  69. { TEXT("country"), TEXT("Country") },
  70. { TEXT("locality"), TEXT("Locality") },
  71. { TEXT("organization"), TEXT("Organization")},
  72. { TEXT("organizationalUnit"), TEXT("Organizational Unit") },
  73. { TEXT("domain"), DOMAIN_CLASS_NAME},
  74. { TEXT("person"), USER_CLASS_NAME },
  75. { TEXT("organizationalPerson"), USER_CLASS_NAME },
  76. { TEXT("residentialPerson"), USER_CLASS_NAME },
  77. { TEXT("groupOfNames"), GROUP_CLASS_NAME },
  78. { TEXT("groupOfUniqueNames"), GROUP_CLASS_NAME }
  79. };
  80. // Class CLDAPGroup
  81. STDMETHODIMP CLDAPGroup::get_Description(THIS_ BSTR FAR* retval)
  82. {
  83. GET_PROPERTY_BSTR((IADsGroup *)this, description);
  84. }
  85. STDMETHODIMP CLDAPGroup::put_Description(THIS_ BSTR bstrdescription)
  86. {
  87. PUT_PROPERTY_BSTR((IADsGroup *)this, description);
  88. }
  89. STDMETHODIMP
  90. CLDAPGroup::Members(
  91. THIS_ IADsMembers FAR* FAR* ppMembers
  92. )
  93. {
  94. VARIANT v;
  95. HRESULT hr = S_OK;
  96. BSTR bstrParent = NULL;
  97. BSTR bstrName = NULL;
  98. BSTR bstrADsPath = NULL;
  99. IADsObjOptPrivate *pPrivOpt = NULL;
  100. BOOL fRangeRetrieval = FALSE;
  101. VariantInit(&v);
  102. hr = get_VARIANT_Property((IADs *) ((IADsGroup *) this),
  103. TEXT("member"),
  104. &v );
  105. if ( hr == E_ADS_PROPERTY_NOT_FOUND )
  106. {
  107. SAFEARRAY *aList = NULL;
  108. SAFEARRAYBOUND aBound;
  109. hr = S_OK;
  110. aBound.lLbound = 0;
  111. aBound.cElements = 0;
  112. aList = SafeArrayCreate( VT_VARIANT, 1, &aBound );
  113. if ( aList == NULL )
  114. {
  115. hr = E_OUTOFMEMORY;
  116. BAIL_ON_FAILURE(hr);
  117. }
  118. V_VT(&v) = VT_ARRAY | VT_VARIANT;
  119. V_ARRAY(&v) = aList;
  120. }
  121. BAIL_ON_FAILURE(hr);
  122. hr = get_Parent( &bstrParent );
  123. BAIL_ON_FAILURE(hr);
  124. hr = get_Name( &bstrName );
  125. BAIL_ON_FAILURE(hr);
  126. hr = _pADs->get_ADsPath( &bstrADsPath);
  127. BAIL_ON_FAILURE(hr);
  128. //
  129. // We need to see if range retrieval was used.
  130. // That info is needed in the enumerator.
  131. //
  132. hr = _pADs->QueryInterface(
  133. IID_IADsObjOptPrivate,
  134. (void **)&pPrivOpt
  135. );
  136. BAIL_ON_FAILURE(hr);
  137. //
  138. // Not a problem if this fails.
  139. //
  140. hr = pPrivOpt->GetOption (
  141. LDAP_MEMBER_HAS_RANGE,
  142. (void *) &fRangeRetrieval
  143. );
  144. hr = CLDAPGroupCollection::CreateGroupCollection(
  145. bstrParent,
  146. bstrADsPath,
  147. bstrName,
  148. &v,
  149. _Credentials,
  150. _pADs,
  151. IID_IADsMembers,
  152. fRangeRetrieval,
  153. (void **)ppMembers
  154. );
  155. BAIL_ON_FAILURE(hr);
  156. error:
  157. if ( bstrParent )
  158. ADsFreeString( bstrParent );
  159. if ( bstrName )
  160. ADsFreeString( bstrName );
  161. if (bstrADsPath) {
  162. ADsFreeString( bstrADsPath);
  163. }
  164. if (pPrivOpt) {
  165. pPrivOpt->Release();
  166. }
  167. VariantClear(&v);
  168. RRETURN(hr);
  169. }
  170. STDMETHODIMP
  171. CLDAPGroup::IsMember(
  172. THIS_ BSTR bstrMember,
  173. VARIANT_BOOL FAR *bMember
  174. )
  175. {
  176. HRESULT hr = S_OK;
  177. if (_dwServerType == SERVER_TYPE_UNKNOWN) {
  178. hr = UpdateServerType();
  179. //
  180. // The only reason the above call shoudl fail is
  181. // if we could not read the ADsPath of the cgenobj.
  182. //
  183. BAIL_ON_FAILURE(hr);
  184. }
  185. if (_dwServerType == SERVER_TYPE_AD) {
  186. hr = IsMemberOnAD(
  187. bstrMember,
  188. bMember
  189. );
  190. }
  191. else {
  192. hr = IsMemberOnOther(
  193. bstrMember,
  194. bMember
  195. );
  196. }
  197. error:
  198. RRETURN(hr);
  199. }
  200. //
  201. // Checks membership if the server is AD. This is because
  202. // we know that AD supports the LDAPCompare operation. There
  203. // is just one round trip on the wire this time.
  204. //
  205. HRESULT
  206. CLDAPGroup::IsMemberOnAD(
  207. THIS_ BSTR bstrMember,
  208. VARIANT_BOOL FAR *bMember
  209. )
  210. {
  211. HRESULT hr = S_OK;
  212. PADSLDP pLdp = NULL;
  213. BSTR bstrParentADsPath = NULL;
  214. IADsObjOptPrivate *pADsPrivateObjectOptions = NULL;
  215. LPWSTR pszGroupServer = NULL, pszGroupDn = NULL;
  216. LPWSTR pszMemberServer = NULL, pszMemberDn = NULL;
  217. DWORD dwGroupPort = 0, dwMemberPort = 0;
  218. if (!bstrMember || !*bstrMember || !bMember) {
  219. BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
  220. }
  221. //
  222. // Default to this value.
  223. //
  224. *bMember = VARIANT_FALSE;
  225. //
  226. // We need the ADsPath of the parent group object.
  227. // Since the input parameter is an ADsPath, we need to
  228. // make sure that the serverName if any matches before
  229. // going onto doing the LDAPCompare operation on the
  230. // DN to verify if the DN is part of member.
  231. //
  232. hr = _pADs->get_ADsPath(&bstrParentADsPath);
  233. BAIL_ON_FAILURE(hr);
  234. //
  235. // Split the path into components we are interesteed in.
  236. //
  237. hr = BuildLDAPPathFromADsPath2(
  238. bstrParentADsPath,
  239. &pszGroupServer,
  240. &pszGroupDn,
  241. &dwGroupPort
  242. );
  243. BAIL_ON_FAILURE(hr);
  244. hr = BuildLDAPPathFromADsPath2(
  245. bstrMember,
  246. &pszMemberServer,
  247. &pszMemberDn,
  248. &dwMemberPort
  249. );
  250. BAIL_ON_FAILURE(hr);
  251. if ((pszMemberServer && !pszGroupServer)
  252. || (pszGroupServer && !pszMemberServer)
  253. || (dwMemberPort != dwGroupPort)
  254. || ( (pszMemberServer && pszGroupServer)
  255. #ifdef WIN95
  256. && (_wcsicmp(pszMemberServer, pszGroupServer))
  257. #else
  258. && (CompareStringW(
  259. LOCALE_SYSTEM_DEFAULT,
  260. NORM_IGNORECASE,
  261. pszMemberServer,
  262. -1,
  263. pszGroupServer,
  264. -1
  265. ) != CSTR_EQUAL)
  266. #endif
  267. )
  268. ) {
  269. //
  270. // Mismatched paths (e.g., bound to group with a serverless
  271. // path, user is passing in a server path)
  272. //
  273. *bMember = VARIANT_FALSE;
  274. hr = E_ADS_BAD_PARAMETER;
  275. goto error;
  276. }
  277. //
  278. // At this point we have a match on the server names and port.
  279. //
  280. hr = _pADs->QueryInterface(
  281. IID_IADsObjOptPrivate,
  282. (void **)&pADsPrivateObjectOptions
  283. );
  284. BAIL_ON_FAILURE(hr);
  285. hr = pADsPrivateObjectOptions->GetOption (
  286. LDP_CACHE_ENTRY,
  287. &pLdp
  288. );
  289. BAIL_ON_FAILURE(hr);
  290. //
  291. // We can now do a LDAPCompare to see if the object is a member.
  292. //
  293. hr = LdapCompareExt(
  294. pLdp,
  295. pszGroupDn,
  296. L"member",
  297. pszMemberDn,
  298. NULL, // Data
  299. NULL, // ClientControls
  300. NULL // ServerControls
  301. );
  302. if (hr == HRESULT_FROM_WIN32(ERROR_DS_COMPARE_FALSE)) {
  303. hr = S_OK;
  304. *bMember = VARIANT_FALSE;
  305. }
  306. else if (hr == HRESULT_FROM_WIN32(ERROR_DS_COMPARE_TRUE)) {
  307. hr = S_OK;
  308. *bMember = VARIANT_TRUE;
  309. } else if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)) {
  310. //
  311. // This is also valid as the member attribute might be empty.
  312. //
  313. hr = S_OK;
  314. *bMember = VARIANT_FALSE;
  315. }
  316. BAIL_ON_FAILURE(hr);
  317. error:
  318. //
  319. // Cleanup all strings that could have been alloced.
  320. //
  321. if (bstrParentADsPath) {
  322. ADsFreeString(bstrParentADsPath);
  323. }
  324. if (pszGroupServer) {
  325. FreeADsStr(pszGroupServer);
  326. }
  327. if (pszGroupDn) {
  328. FreeADsStr(pszGroupDn);
  329. }
  330. if (pszMemberServer) {
  331. FreeADsStr(pszMemberServer);
  332. }
  333. if (pszMemberDn) {
  334. FreeADsStr(pszMemberDn);
  335. }
  336. //
  337. // Miscellaneous cleanup.
  338. //
  339. if (pADsPrivateObjectOptions) {
  340. pADsPrivateObjectOptions->Release();
  341. }
  342. RRETURN(hr);
  343. }
  344. //
  345. // This routine is used if the server is not AD - preserves
  346. // older behaviour. It creates an Enumerator, goes through that
  347. // comparing the paths to see if there is a match. This is
  348. // pretty network intensive.
  349. //
  350. HRESULT
  351. CLDAPGroup::IsMemberOnOther(
  352. THIS_ BSTR bstrMember,
  353. VARIANT_BOOL FAR* bMember
  354. )
  355. {
  356. IADsMembers FAR * pMembers = NULL;
  357. IUnknown FAR * pUnknown = NULL;
  358. IEnumVARIANT FAR * pEnumVar = NULL;
  359. DWORD i = 0;
  360. HRESULT hr = S_OK;
  361. VARIANT_BOOL fMember = FALSE;
  362. VARIANT VariantArray[10];
  363. BOOL fContinue = TRUE;
  364. ULONG cElementFetched = 0;
  365. hr = Members(
  366. &pMembers
  367. );
  368. BAIL_ON_FAILURE(hr);
  369. hr = pMembers->get__NewEnum(
  370. &pUnknown
  371. );
  372. BAIL_ON_FAILURE(hr);
  373. hr = pUnknown->QueryInterface(
  374. IID_IEnumVARIANT,
  375. (void **)&pEnumVar
  376. );
  377. BAIL_ON_FAILURE(hr);
  378. while (fContinue) {
  379. IADs *pObject ;
  380. hr = pEnumVar->Next(
  381. 10,
  382. VariantArray,
  383. &cElementFetched
  384. );
  385. if (hr == S_FALSE) {
  386. fContinue = FALSE;
  387. //
  388. // Reset hr to S_OK, we want to return success
  389. //
  390. hr = S_OK;
  391. }
  392. fMember = (VARIANT_BOOL)VerifyIfMember(
  393. bstrMember,
  394. VariantArray,
  395. cElementFetched
  396. );
  397. if (fMember) {
  398. fContinue = FALSE;
  399. }
  400. for (i = 0; i < cElementFetched; i++ ) {
  401. IDispatch *pDispatch = NULL;
  402. pDispatch = VariantArray[i].pdispVal;
  403. pDispatch->Release();
  404. }
  405. memset(VariantArray, 0, sizeof(VARIANT)*10);
  406. }
  407. error:
  408. *bMember = fMember? VARIANT_TRUE : VARIANT_FALSE;
  409. if (pEnumVar) {
  410. pEnumVar->Release();
  411. }
  412. if (pUnknown) {
  413. pUnknown->Release();
  414. }
  415. if (pMembers) {
  416. pMembers->Release();
  417. }
  418. RRETURN(hr);
  419. }
  420. BOOL
  421. VerifyIfMember(
  422. BSTR bstrMember,
  423. VARIANT * VariantArray,
  424. ULONG cElementFetched
  425. )
  426. {
  427. DWORD i = 0;
  428. HRESULT hr = S_OK;
  429. IADs FAR * pObject = NULL;
  430. IDispatch FAR * pDispatch = NULL;
  431. for (i = 0; i < cElementFetched; i++ ) {
  432. IDispatch *pDispatch = NULL;
  433. BSTR bstrName = NULL;
  434. pDispatch = VariantArray[i].pdispVal;
  435. hr = pDispatch->QueryInterface(
  436. IID_IADs,
  437. (VOID **) &pObject
  438. );
  439. BAIL_ON_FAILURE(hr);
  440. hr = pObject->get_ADsPath(&bstrName);
  441. BAIL_ON_FAILURE(hr);
  442. #ifdef WIN95
  443. if (!_wcsicmp(bstrName, bstrMember)) {
  444. #else
  445. if (CompareStringW(
  446. LOCALE_SYSTEM_DEFAULT,
  447. NORM_IGNORECASE,
  448. bstrName,
  449. -1,
  450. bstrMember,
  451. -1
  452. ) == CSTR_EQUAL
  453. ) {
  454. #endif
  455. SysFreeString(bstrName);
  456. bstrName = NULL;
  457. pObject->Release();
  458. return(TRUE);
  459. }
  460. SysFreeString(bstrName);
  461. bstrName = NULL;
  462. pObject->Release();
  463. }
  464. error:
  465. return(FALSE);
  466. }
  467. STDMETHODIMP
  468. CLDAPGroup::Add(THIS_ BSTR bstrNewItem)
  469. {
  470. RRETURN( ModifyGroup(bstrNewItem, TRUE ));
  471. }
  472. STDMETHODIMP
  473. CLDAPGroup::Remove(THIS_ BSTR bstrItemToBeRemoved)
  474. {
  475. RRETURN( ModifyGroup(bstrItemToBeRemoved, FALSE ));
  476. }
  477. HRESULT
  478. CLDAPGroup::ModifyGroup( THIS_ BSTR bstrItem, BOOL fAdd )
  479. {
  480. HRESULT hr = S_OK;
  481. DWORD dwStatus = 0L;
  482. TCHAR *pszLDAPServer = NULL;
  483. TCHAR *pszItemLDAPServer = NULL;
  484. TCHAR *pszLDAPDn = NULL;
  485. TCHAR *pszItemLDAPDn = NULL;
  486. BSTR bstrADsPath = NULL;
  487. DWORD dwSyntaxId;
  488. ADS_LDP * ld = NULL;
  489. LDAPModW *aMod[2];
  490. LDAPModW ldapmod;
  491. WCHAR *aStrings[2];
  492. OBJECTINFO ObjectInfo;
  493. POBJECTINFO pObjectInfo = &ObjectInfo;
  494. DWORD dwPort = 0;
  495. if (!bstrItem || !*bstrItem) {
  496. RRETURN(E_FAIL);
  497. }
  498. memset(pObjectInfo, 0, sizeof(OBJECTINFO));
  499. pObjectInfo->ObjectType = TOKEN_LDAPOBJECT;
  500. hr = ADsObject(bstrItem, pObjectInfo);
  501. BAIL_ON_FAILURE(hr);
  502. hr = ValidateProvider(pObjectInfo);
  503. BAIL_ON_FAILURE(hr);
  504. hr = BuildLDAPPathFromADsPath2(
  505. bstrItem,
  506. &pszItemLDAPServer,
  507. &pszItemLDAPDn,
  508. &dwPort
  509. );
  510. BAIL_ON_FAILURE(hr);
  511. hr = get_ADsPath( &bstrADsPath );
  512. BAIL_ON_FAILURE(hr);
  513. hr = BuildLDAPPathFromADsPath2(
  514. bstrADsPath,
  515. &pszLDAPServer,
  516. &pszLDAPDn,
  517. &dwPort
  518. );
  519. BAIL_ON_FAILURE(hr);
  520. hr = LdapGetSyntaxOfAttributeOnServer(
  521. pszLDAPServer,
  522. TEXT("member"),
  523. &dwSyntaxId,
  524. _Credentials,
  525. pObjectInfo->PortNumber
  526. );
  527. BAIL_ON_FAILURE(hr);
  528. hr = LdapOpenObject(
  529. pszLDAPServer,
  530. pszLDAPDn,
  531. &ld,
  532. _Credentials,
  533. dwPort
  534. );
  535. BAIL_ON_FAILURE(hr);
  536. aMod[0] = &ldapmod;
  537. aMod[1] = NULL;
  538. aStrings[0] = pszItemLDAPDn;
  539. aStrings[1] = NULL;
  540. ldapmod.mod_type = L"member";
  541. ldapmod.mod_values = aStrings;
  542. ldapmod.mod_op = fAdd? LDAP_MOD_ADD : LDAP_MOD_DELETE;
  543. dwStatus = LdapModifyS(
  544. ld,
  545. pszLDAPDn,
  546. aMod
  547. );
  548. if (dwStatus) {
  549. hr = HRESULT_FROM_WIN32(dwStatus);
  550. BAIL_ON_FAILURE(hr);
  551. }
  552. error:
  553. FreeObjectInfo( &ObjectInfo );
  554. if (pszItemLDAPServer)
  555. FreeADsStr( pszItemLDAPServer );
  556. if (pszItemLDAPDn) {
  557. FreeADsStr(pszItemLDAPDn);
  558. }
  559. if (pszLDAPDn) {
  560. FreeADsStr(pszLDAPDn);
  561. }
  562. if (pszLDAPServer)
  563. FreeADsStr( pszLDAPServer );
  564. if (bstrADsPath)
  565. ADsFreeString( bstrADsPath );
  566. if (ld) {
  567. LdapCloseObject(ld);
  568. }
  569. RRETURN(hr);
  570. }
  571. HRESULT
  572. CLDAPGroup::UpdateServerType()
  573. {
  574. HRESULT hr = S_OK;
  575. BSTR bstrADsPath = NULL;
  576. LPWSTR pszGroupServer = NULL;
  577. LPWSTR pszGroupDn = NULL;
  578. BOOL fServerIsAD = FALSE;
  579. DWORD dwGroupPort = 0;
  580. //
  581. // Read the servertype only if we have not already done so.
  582. //
  583. if (_dwServerType == SERVER_TYPE_UNKNOWN) {
  584. hr = _pADs->get_ADsPath( &bstrADsPath);
  585. BAIL_ON_FAILURE(hr);
  586. hr = BuildLDAPPathFromADsPath2(
  587. bstrADsPath,
  588. &pszGroupServer,
  589. &pszGroupDn,
  590. &dwGroupPort
  591. );
  592. BAIL_ON_FAILURE(hr);
  593. hr = ReadServerSupportsIsADControl(
  594. pszGroupServer,
  595. &fServerIsAD,
  596. _Credentials,
  597. dwGroupPort
  598. );
  599. //
  600. // Treat failure to mean server is not AD
  601. //
  602. if (FAILED(hr)) {
  603. fServerIsAD = FALSE;
  604. hr = S_OK;
  605. }
  606. if (fServerIsAD) {
  607. _dwServerType = SERVER_TYPE_AD;
  608. }
  609. else {
  610. _dwServerType = SERVER_TYPE_NOT_AD;
  611. }
  612. }
  613. error:
  614. if (bstrADsPath) {
  615. ADsFreeString(bstrADsPath);
  616. }
  617. if (pszGroupServer) {
  618. FreeADsStr(pszGroupServer);
  619. }
  620. if (pszGroupDn) {
  621. FreeADsStr(pszGroupDn);
  622. }
  623. RRETURN(hr);
  624. }
  625. HRESULT
  626. ValidateProvider(
  627. POBJECTINFO pObjectInfo
  628. )
  629. {
  630. //
  631. // The provider name is case-sensitive. This is a restriction that OLE
  632. // has put on us.
  633. //
  634. if (_tcscmp(pObjectInfo->ProviderName, L"LDAP") == 0) {
  635. RRETURN(S_OK);
  636. }
  637. RRETURN(E_FAIL);
  638. }
  639. BOOL
  640. MapLdapClassToADsClass(
  641. LPTSTR *aLdapClasses,
  642. int nCount,
  643. LPTSTR pszADsClass
  644. )
  645. {
  646. *pszADsClass = 0;
  647. if ( nCount == 0 )
  648. return FALSE;
  649. if ( _tcsicmp( aLdapClasses[nCount-1], TEXT("Top")) == 0 )
  650. {
  651. for ( int j = 0; j < nCount; j++ )
  652. {
  653. LPTSTR pszLdapClass = aLdapClasses[j];
  654. for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ )
  655. {
  656. if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 )
  657. {
  658. _tcscpy( pszADsClass, aClassMap[i].pszADsClassName );
  659. return TRUE;
  660. }
  661. }
  662. }
  663. _tcscpy( pszADsClass, aLdapClasses[0] );
  664. return FALSE;
  665. }
  666. else
  667. {
  668. for ( int j = nCount-1; j >= 0; j-- )
  669. {
  670. LPTSTR pszLdapClass = aLdapClasses[j];
  671. for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ )
  672. {
  673. if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 )
  674. {
  675. _tcscpy( pszADsClass, aClassMap[i].pszADsClassName );
  676. return TRUE;
  677. }
  678. }
  679. }
  680. _tcscpy( pszADsClass, aLdapClasses[nCount-1] );
  681. return FALSE;
  682. }
  683. }