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.

820 lines
18 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1996
  5. //
  6. // File: util.cxx
  7. //
  8. // Contents: Some misc helper functions
  9. //
  10. // History:
  11. //----------------------------------------------------------------------------
  12. #include "ldapc.hxx"
  13. #pragma hdrstop
  14. BOOL
  15. IsContainer(
  16. LPTSTR pszClassName,
  17. LDAP_SCHEMA_HANDLE hSchema
  18. );
  19. /******************************************************************/
  20. /* Class SCHEMAINFO
  21. /******************************************************************/
  22. SCHEMAINFO::SCHEMAINFO()
  23. : _cRef( 0 ),
  24. _fObsolete( FALSE ),
  25. fDefaultSchema( FALSE ),
  26. fAppearsV3(TRUE),
  27. pszServerName( NULL ),
  28. pszSubSchemaSubEntry( NULL ),
  29. pszTime( NULL ),
  30. Next( NULL ),
  31. aClasses( NULL ),
  32. nNumOfClasses( 0 ),
  33. aClassesSearchTable( NULL ),
  34. aProperties( NULL ),
  35. nNumOfProperties( 0 ),
  36. aPropertiesSearchTable( NULL ),
  37. pszUserName( NULL )
  38. {
  39. }
  40. SCHEMAINFO::~SCHEMAINFO()
  41. {
  42. if ( pszServerName )
  43. FreeADsStr( pszServerName );
  44. if ( pszUserName )
  45. FreeADsStr( pszUserName );
  46. if ( pszSubSchemaSubEntry )
  47. FreeADsStr( pszSubSchemaSubEntry );
  48. if ( pszTime )
  49. FreeADsStr( pszTime );
  50. if ( !fDefaultSchema )
  51. {
  52. if ( aClasses )
  53. FreeClassInfoArray( aClasses, nNumOfClasses );
  54. if ( aClassesSearchTable )
  55. FreeADsMem( aClassesSearchTable );
  56. if ( aProperties )
  57. FreePropertyInfoArray( aProperties, nNumOfProperties );
  58. if ( aPropertiesSearchTable )
  59. FreeADsMem( aPropertiesSearchTable );
  60. }
  61. }
  62. DWORD SCHEMAINFO::AddRef()
  63. {
  64. return ++_cRef;
  65. }
  66. DWORD SCHEMAINFO::Release()
  67. {
  68. if ( _cRef > 0 )
  69. return --_cRef;
  70. return 0;
  71. }
  72. //
  73. // Helper routine that looks up the syntax tables (oid and name oid)
  74. // and returns the correct syntax corresponding to the string name.
  75. //
  76. DWORD
  77. LdapGetSyntaxIdOfAttribute(
  78. LPWSTR pszStringSyntax
  79. )
  80. {
  81. HRESULT hr = S_OK;
  82. DWORD dwSyntaxId = -1;
  83. dwSyntaxId = FindEntryInSearchTable(
  84. pszStringSyntax,
  85. g_aSyntaxSearchTable,
  86. g_nSyntaxSearchTableSize
  87. );
  88. if (dwSyntaxId == -1) {
  89. //
  90. // We also need to search in the OID based syntax table.
  91. //
  92. dwSyntaxId = FindEntryInSearchTable(
  93. pszStringSyntax,
  94. g_aOidSyntaxSearchTable,
  95. g_nOidSyntaxSearchTableSize
  96. );
  97. }
  98. return dwSyntaxId;
  99. }
  100. HRESULT
  101. LdapGetSyntaxOfAttributeOnServerHelper(
  102. LPTSTR pszServerPath,
  103. LPTSTR pszAttrName,
  104. DWORD *pdwSyntaxId,
  105. CCredentials& Credentials,
  106. DWORD dwPort
  107. )
  108. {
  109. HRESULT hr = S_OK;
  110. SCHEMAINFO *pSchemaInfo = NULL;
  111. DWORD dwEntry;
  112. LPWSTR pszTemp = NULL;
  113. *pdwSyntaxId = LDAPTYPE_UNKNOWN;
  114. hr = LdapGetSchema( pszServerPath,
  115. &pSchemaInfo,
  116. Credentials,
  117. dwPort
  118. );
  119. BAIL_IF_ERROR(hr);
  120. // Support for range attributes; for eg., objectClass=Range=0-1 We should
  121. // ignore everything after ';' inclusive.
  122. //
  123. if ((pszTemp = wcschr(pszAttrName, L';')) != NULL ) {
  124. *pszTemp = L'\0';
  125. }
  126. dwEntry = FindEntryInSearchTable(
  127. pszAttrName,
  128. pSchemaInfo->aPropertiesSearchTable,
  129. pSchemaInfo->nNumOfProperties * 2 );
  130. //
  131. // Put back the ; if we had replaced it.
  132. //
  133. if (pszTemp)
  134. *pszTemp = L';';
  135. if ( dwEntry != -1 )
  136. {
  137. //
  138. // This helper routine will lookup both the oid table and the
  139. // name based syntax table and return -1 if unsuccesful.
  140. //
  141. *pdwSyntaxId = LdapGetSyntaxIdOfAttribute(
  142. pSchemaInfo->aProperties[dwEntry].pszSyntax
  143. );
  144. if ( *pdwSyntaxId == -1 ) {
  145. *pdwSyntaxId = LDAPTYPE_UNKNOWN;
  146. }
  147. }
  148. else
  149. {
  150. hr = E_ADS_PROPERTY_NOT_FOUND;
  151. BAIL_IF_ERROR(hr);
  152. }
  153. cleanup:
  154. if ( pSchemaInfo )
  155. pSchemaInfo->Release();
  156. RRETURN(hr);
  157. }
  158. //
  159. // This routine calls the helper and if the flag FromServer is TRUE,
  160. // then if we cannot find the syntax on the server then we will
  161. // mark as obsolete and retry. This will fix some not so obvious
  162. // cases of problems with the schema across mutliple DC's. The
  163. // underlying assumption is that if the server sent the info, then
  164. // it should have the schema information to match.
  165. //
  166. HRESULT
  167. LdapGetSyntaxOfAttributeOnServer(
  168. LPTSTR pszServerPath,
  169. LPTSTR pszAttrName,
  170. DWORD *pdwSyntaxId,
  171. CCredentials& Credentials,
  172. DWORD dwPort,
  173. BOOL fFromServer // defaulted to FALSE
  174. )
  175. {
  176. HRESULT hr = S_OK;
  177. hr = LdapGetSyntaxOfAttributeOnServerHelper(
  178. pszServerPath,
  179. pszAttrName,
  180. pdwSyntaxId,
  181. Credentials,
  182. dwPort
  183. );
  184. //
  185. // Reset and retry only if fFromServer is true, and
  186. // the failure was E_ADS_PROPERTY_NOT_FOUND. If this is
  187. // a v2 server then there will be no significant perf hit
  188. // as we do not refresh the default schema.
  189. //
  190. if (FAILED(hr)
  191. && (hr == E_ADS_PROPERTY_NOT_FOUND)
  192. && fFromServer) {
  193. //
  194. // Mark schema as old.
  195. //
  196. hr = LdapRemoveSchemaInfoOnServer(
  197. pszServerPath,
  198. Credentials,
  199. dwPort,
  200. TRUE // force update.
  201. );
  202. BAIL_ON_FAILURE(hr);
  203. hr = LdapGetSyntaxOfAttributeOnServerHelper(
  204. pszServerPath,
  205. pszAttrName,
  206. pdwSyntaxId,
  207. Credentials,
  208. dwPort
  209. );
  210. BAIL_ON_FAILURE(hr);
  211. }
  212. else {
  213. //
  214. // This is the normal exit path.
  215. //
  216. RRETURN(hr);
  217. }
  218. error :
  219. //
  220. // If we get here we need to return prop not found
  221. // other code may depend on that. Note that we will come
  222. // here only if we the first try failed and something went
  223. // wrong while trying to force a reload of the schema.
  224. //
  225. if (FAILED(hr)) {
  226. RRETURN(hr = E_ADS_PROPERTY_NOT_FOUND);
  227. }
  228. else {
  229. RRETURN(hr);
  230. }
  231. }
  232. HRESULT
  233. LdapIsClassNameValidOnServer(
  234. LPTSTR pszServerPath,
  235. LPTSTR pszClassName,
  236. BOOL *pfValid,
  237. CCredentials& Credentials,
  238. DWORD dwPort
  239. )
  240. {
  241. HRESULT hr = S_OK;
  242. SCHEMAINFO *pSchemaInfo = NULL;
  243. *pfValid = FALSE;
  244. hr = LdapGetSchema( pszServerPath,
  245. &pSchemaInfo,
  246. Credentials,
  247. dwPort
  248. );
  249. BAIL_IF_ERROR(hr);
  250. if ( FindEntryInSearchTable(
  251. pszClassName,
  252. pSchemaInfo->aClassesSearchTable,
  253. pSchemaInfo->nNumOfClasses * 2 ) != -1 )
  254. {
  255. *pfValid = TRUE;
  256. }
  257. cleanup:
  258. if ( pSchemaInfo )
  259. pSchemaInfo->Release();
  260. RRETURN(hr);
  261. }
  262. HRESULT
  263. LdapGetSchemaObjectCount(
  264. LPTSTR pszServerPath,
  265. DWORD *pnNumOfClasses,
  266. DWORD *pnNumOfProperties,
  267. CCredentials& Credentials,
  268. DWORD dwPort
  269. )
  270. {
  271. HRESULT hr = S_OK;
  272. SCHEMAINFO *pSchemaInfo = NULL;
  273. hr = LdapGetSchema( pszServerPath,
  274. &pSchemaInfo,
  275. Credentials,
  276. dwPort
  277. );
  278. BAIL_IF_ERROR(hr);
  279. *pnNumOfClasses = pSchemaInfo->nNumOfClasses;
  280. *pnNumOfProperties = pSchemaInfo->nNumOfProperties;
  281. cleanup:
  282. if ( pSchemaInfo )
  283. pSchemaInfo->Release();
  284. RRETURN(hr);
  285. }
  286. HRESULT
  287. LdapGetSubSchemaSubEntryPath(
  288. LPTSTR pszServerPath,
  289. LPTSTR *ppszSubSchemaSubEntryPath,
  290. CCredentials& Credentials,
  291. DWORD dwPort
  292. )
  293. {
  294. HRESULT hr = S_OK;
  295. SCHEMAINFO *pSchemaInfo = NULL;
  296. *ppszSubSchemaSubEntryPath = NULL;
  297. hr = LdapGetSchema( pszServerPath,
  298. &pSchemaInfo,
  299. Credentials,
  300. dwPort
  301. );
  302. BAIL_IF_ERROR(hr);
  303. if ( pSchemaInfo->pszSubSchemaSubEntry )
  304. {
  305. *ppszSubSchemaSubEntryPath =
  306. AllocADsStr( pSchemaInfo->pszSubSchemaSubEntry );
  307. if ( *ppszSubSchemaSubEntryPath == NULL )
  308. {
  309. hr = E_OUTOFMEMORY;
  310. BAIL_IF_ERROR(hr);
  311. }
  312. }
  313. cleanup:
  314. if ( pSchemaInfo )
  315. pSchemaInfo->Release();
  316. RRETURN(hr);
  317. }
  318. HRESULT
  319. LdapMakeSchemaCacheObsolete(
  320. LPTSTR pszServerPath,
  321. CCredentials& Credentials,
  322. DWORD dwPort
  323. )
  324. {
  325. RRETURN( LdapRemoveSchemaInfoOnServer(
  326. pszServerPath,
  327. Credentials,
  328. dwPort
  329. )
  330. );
  331. }
  332. HRESULT
  333. SchemaOpen(
  334. IN LPTSTR pszServerPath,
  335. OUT LDAP_SCHEMA_HANDLE *phSchema,
  336. IN CCredentials& Credentials,
  337. DWORD dwPort
  338. )
  339. {
  340. HRESULT hr = S_OK;
  341. SCHEMAINFO *pSchemaInfo = NULL;
  342. *phSchema = NULL;
  343. hr = LdapGetSchema( pszServerPath,
  344. &pSchemaInfo,
  345. Credentials,
  346. dwPort
  347. );
  348. if ( FAILED(hr))
  349. RRETURN(hr);
  350. *phSchema = (HANDLE) pSchemaInfo;
  351. RRETURN(S_OK);
  352. }
  353. HRESULT
  354. SchemaClose(
  355. IN OUT LDAP_SCHEMA_HANDLE *phSchema
  356. )
  357. {
  358. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) *phSchema;
  359. if ( !pSchemaInfo )
  360. RRETURN(E_ADS_BAD_PARAMETER);
  361. if ( pSchemaInfo->Release() == 0 )
  362. *phSchema = NULL;
  363. RRETURN(S_OK);
  364. }
  365. HRESULT
  366. SchemaAddRef(
  367. IN LDAP_SCHEMA_HANDLE hSchema
  368. )
  369. {
  370. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  371. if ( !pSchemaInfo )
  372. RRETURN(E_ADS_BAD_PARAMETER);
  373. pSchemaInfo->AddRef();
  374. RRETURN(S_OK);
  375. }
  376. HRESULT
  377. SchemaGetObjectCount(
  378. LDAP_SCHEMA_HANDLE hSchema,
  379. DWORD *pnNumOfClasses,
  380. DWORD *pnNumOfProperties
  381. )
  382. {
  383. HRESULT hr = S_OK;
  384. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  385. if ( !pSchemaInfo )
  386. RRETURN(E_ADS_BAD_PARAMETER);
  387. *pnNumOfClasses = pSchemaInfo->nNumOfClasses;
  388. *pnNumOfProperties = pSchemaInfo->nNumOfProperties;
  389. RRETURN(hr);
  390. }
  391. HRESULT
  392. SchemaGetClassInfoByIndex(
  393. LDAP_SCHEMA_HANDLE hSchema,
  394. DWORD dwIndex,
  395. CLASSINFO **ppClassInfo
  396. )
  397. {
  398. HRESULT hr = S_OK;
  399. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  400. if ( !pSchemaInfo )
  401. RRETURN(E_ADS_BAD_PARAMETER);
  402. *ppClassInfo = &(pSchemaInfo->aClasses[dwIndex]);
  403. RRETURN(hr);
  404. }
  405. HRESULT
  406. SchemaGetPropertyInfoByIndex(
  407. LDAP_SCHEMA_HANDLE hSchema,
  408. DWORD dwIndex,
  409. PROPERTYINFO **ppPropertyInfo
  410. )
  411. {
  412. HRESULT hr = S_OK;
  413. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  414. if ( !pSchemaInfo )
  415. RRETURN(E_ADS_BAD_PARAMETER);
  416. *ppPropertyInfo = &(pSchemaInfo->aProperties[dwIndex]);
  417. RRETURN(hr);
  418. }
  419. HRESULT
  420. SchemaGetClassInfo(
  421. LDAP_SCHEMA_HANDLE hSchema,
  422. LPTSTR pszClassName,
  423. CLASSINFO **ppClassInfo
  424. )
  425. {
  426. HRESULT hr = S_OK;
  427. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  428. DWORD dwIndex = (DWORD) -1;
  429. if ( !pSchemaInfo )
  430. RRETURN(E_ADS_BAD_PARAMETER);
  431. dwIndex = FindEntryInSearchTable(
  432. pszClassName,
  433. pSchemaInfo->aClassesSearchTable,
  434. pSchemaInfo->nNumOfClasses * 2 );
  435. if ( dwIndex == -1 )
  436. {
  437. *ppClassInfo = NULL;
  438. }
  439. else
  440. {
  441. *ppClassInfo = &(pSchemaInfo->aClasses[dwIndex]);
  442. }
  443. RRETURN(hr);
  444. }
  445. HRESULT
  446. SchemaGetPropertyInfo(
  447. LDAP_SCHEMA_HANDLE hSchema,
  448. LPTSTR pszPropertyName,
  449. PROPERTYINFO **ppPropertyInfo
  450. )
  451. {
  452. HRESULT hr = S_OK;
  453. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  454. DWORD dwIndex = (DWORD) -1;
  455. LPWSTR pszTemp = NULL;
  456. if ( !pSchemaInfo )
  457. RRETURN(E_ADS_BAD_PARAMETER);
  458. // Support for range attributes; for eg., objectClass=Range=0-1 We should
  459. // ignore everything after ';' inclusive.
  460. //
  461. if ((pszTemp = wcschr(pszPropertyName, L';')) != NULL ) {
  462. *pszTemp = L'\0';
  463. }
  464. dwIndex = FindEntryInSearchTable(
  465. pszPropertyName,
  466. pSchemaInfo->aPropertiesSearchTable,
  467. pSchemaInfo->nNumOfProperties * 2 );
  468. //
  469. // Put back the ; if we had replaced it.
  470. //
  471. if (pszTemp)
  472. *pszTemp = L';';
  473. if ( dwIndex == -1 )
  474. {
  475. *ppPropertyInfo = NULL;
  476. }
  477. else
  478. {
  479. *ppPropertyInfo = &(pSchemaInfo->aProperties[dwIndex]);
  480. }
  481. RRETURN(hr);
  482. }
  483. HRESULT
  484. SchemaGetSyntaxOfAttribute(
  485. LDAP_SCHEMA_HANDLE hSchema,
  486. LPTSTR pszAttrName,
  487. DWORD *pdwSyntaxId
  488. )
  489. {
  490. HRESULT hr = S_OK;
  491. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  492. LPWSTR pszTemp = NULL;
  493. if ( !pSchemaInfo )
  494. RRETURN(E_ADS_BAD_PARAMETER);
  495. *pdwSyntaxId = LDAPTYPE_UNKNOWN;
  496. // Support for range attributes; for eg., objectClass=Range=0-1 We should
  497. // ignore everything after ';' inclusive.
  498. //
  499. if ((pszTemp = wcschr(pszAttrName, L';')) != NULL ) {
  500. *pszTemp = L'\0';
  501. }
  502. DWORD dwEntry = FindEntryInSearchTable(
  503. pszAttrName,
  504. pSchemaInfo->aPropertiesSearchTable,
  505. pSchemaInfo->nNumOfProperties * 2 );
  506. //
  507. // Put back the ; if we had replaced it.
  508. //
  509. if (pszTemp)
  510. *pszTemp = L';';
  511. if ( dwEntry != -1 )
  512. {
  513. *pdwSyntaxId = FindEntryInSearchTable(
  514. pSchemaInfo->aProperties[dwEntry].pszSyntax,
  515. g_aSyntaxSearchTable,
  516. g_nSyntaxSearchTableSize );
  517. if ( *pdwSyntaxId == -1 ) {
  518. //
  519. // We also need to search in the OID based syntax table.
  520. //
  521. *pdwSyntaxId = FindEntryInSearchTable(
  522. pSchemaInfo->aProperties[dwEntry].pszSyntax,
  523. g_aOidSyntaxSearchTable,
  524. g_nOidSyntaxSearchTableSize
  525. );
  526. if ( *pdwSyntaxId == -1 ) {
  527. *pdwSyntaxId = LDAPTYPE_UNKNOWN;
  528. }
  529. }
  530. }
  531. else
  532. {
  533. hr = E_ADS_PROPERTY_NOT_FOUND;
  534. BAIL_IF_ERROR(hr);
  535. }
  536. cleanup:
  537. RRETURN(hr);
  538. }
  539. HRESULT
  540. SchemaIsClassAContainer(
  541. LDAP_SCHEMA_HANDLE hSchema,
  542. LPTSTR pszClassName,
  543. BOOL *pfContainer
  544. )
  545. {
  546. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  547. if ( !pSchemaInfo )
  548. RRETURN(E_ADS_BAD_PARAMETER);
  549. if ( ( _tcsicmp( pszClassName, TEXT("Container")) == 0 )
  550. || ( _tcsicmp( pszClassName, TEXT("organizationalUnit")) == 0 )
  551. || ( _tcsicmp( pszClassName, TEXT("organization")) == 0 )
  552. || ( _tcsicmp( pszClassName, TEXT("country")) == 0 )
  553. || ( _tcsicmp( pszClassName, TEXT("locality")) == 0 )
  554. || ( _tcsicmp( pszClassName, TEXT("device")) == 0 )
  555. || ( _tcsicmp( pszClassName, TEXT("DMD")) == 0 )
  556. || ( _tcsicmp( pszClassName, TEXT("mSFTDSA")) == 0 )
  557. || ( _tcsicmp( pszClassName, TEXT("Domain")) == 0 )
  558. )
  559. {
  560. *pfContainer = TRUE;
  561. RRETURN(S_OK);
  562. }
  563. *pfContainer = IsContainer( pszClassName, hSchema );
  564. RRETURN(S_OK);
  565. }
  566. HRESULT
  567. SchemaGetStringsFromStringTable(
  568. LDAP_SCHEMA_HANDLE hSchema,
  569. int *propList,
  570. DWORD nCount,
  571. LPWSTR **paStrings
  572. )
  573. {
  574. HRESULT hr = S_OK;
  575. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  576. long i = 0;
  577. if ( !pSchemaInfo )
  578. RRETURN(E_ADS_BAD_PARAMETER);
  579. if ( (propList != NULL) && (*propList != -1) )
  580. {
  581. *paStrings = (LPWSTR *) AllocADsMem( (nCount+1)*sizeof(LPWSTR));
  582. if ( *paStrings == NULL )
  583. {
  584. hr = E_OUTOFMEMORY;
  585. BAIL_ON_FAILURE(hr);
  586. }
  587. i = 0;
  588. while ( propList[i] != -1 )
  589. {
  590. (*paStrings)[i] = AllocADsStr(
  591. pSchemaInfo->aProperties[pSchemaInfo->aPropertiesSearchTable[propList[i]].nIndex].pszPropertyName );
  592. if ( (*paStrings)[i] == NULL )
  593. {
  594. hr = E_OUTOFMEMORY;
  595. BAIL_ON_FAILURE(hr);
  596. }
  597. i++;
  598. }
  599. (*paStrings)[i] = NULL;
  600. }
  601. else
  602. {
  603. *paStrings = NULL;
  604. }
  605. return S_OK;
  606. error:
  607. if ( *paStrings )
  608. {
  609. i = 0;
  610. while ( (*paStrings)[i] )
  611. {
  612. FreeADsStr( (*paStrings)[i] );
  613. i++;
  614. }
  615. FreeADsMem( *paStrings );
  616. }
  617. RRETURN(hr);
  618. }
  619. BOOL
  620. IsContainer(
  621. LPTSTR pszClassName,
  622. LDAP_SCHEMA_HANDLE hSchema
  623. )
  624. {
  625. int i = 0;
  626. CLASSINFO *pClassInfo;
  627. LPTSTR pszName;
  628. DWORD index;
  629. SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema;
  630. if ( ( _tcsicmp( pszClassName, TEXT("Container")) == 0 )
  631. || ( _tcsicmp( pszClassName, TEXT("organizationalUnit")) == 0 )
  632. || ( _tcsicmp( pszClassName, TEXT("organization")) == 0 )
  633. || ( _tcsicmp( pszClassName, TEXT("country")) == 0 )
  634. || ( _tcsicmp( pszClassName, TEXT("locality")) == 0 )
  635. || ( _tcsicmp( pszClassName, TEXT("device")) == 0 )
  636. || ( _tcsicmp( pszClassName, TEXT("DMD")) == 0 )
  637. || ( _tcsicmp( pszClassName, TEXT("mSFTDSA")) == 0 )
  638. || ( _tcsicmp( pszClassName, TEXT("Domain")) == 0 )
  639. )
  640. {
  641. return TRUE;
  642. }
  643. index = (DWORD) FindEntryInSearchTable(
  644. pszClassName,
  645. pSchemaInfo->aClassesSearchTable,
  646. 2 * pSchemaInfo->nNumOfClasses );
  647. if ( i == ((DWORD) -1) )
  648. return FALSE;
  649. pClassInfo = &(pSchemaInfo->aClasses[index]);
  650. if ( pClassInfo->pOIDsSuperiorClasses )
  651. {
  652. for ( i = 0;
  653. (pszName = pClassInfo->pOIDsSuperiorClasses[i]);
  654. i++ )
  655. {
  656. if ( IsContainer( pszName, hSchema ))
  657. return TRUE;
  658. }
  659. }
  660. if ( pClassInfo->pOIDsAuxClasses )
  661. {
  662. for ( i = 0;
  663. (pszName = pClassInfo->pOIDsAuxClasses[i]);
  664. i++ )
  665. {
  666. if ( IsContainer( pszName, hSchema ))
  667. return TRUE;
  668. }
  669. }
  670. return FALSE;
  671. }