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

7296 lines
196 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1996
  5. //
  6. // File: ldapsch.cxx
  7. //
  8. // Contents: LDAP Schema Parser
  9. //
  10. // History:
  11. //----------------------------------------------------------------------------
  12. #include "ldapc.hxx"
  13. #pragma hdrstop
  14. #define ADSI_LDAP_KEY TEXT("SOFTWARE\\Microsoft\\ADs\\Providers\\LDAP")
  15. #define SCHEMA_DIR_NAME TEXT("SchCache\\")
  16. #define SCHEMA_FILE_NAME_EXT TEXT(".sch")
  17. #define DEFAULT_SCHEMA_FILE_NAME TEXT("Default")
  18. #define DEFAULT_SCHEMA_FILE_NAME_WITH_EXT TEXT("Default.sch")
  19. #define SCHEMA_FILE_NAME TEXT("File")
  20. #define SCHEMA_TIME TEXT("Time")
  21. #define SCHEMA_PROCESSAUX TEXT("ProcessAUX")
  22. #define MAX_LOOP_COUNT 30 // Maximum depth of schema class tree
  23. #define ENTER_SCHEMA_CRITSECT() EnterCriticalSection(&g_SchemaCritSect)
  24. #define LEAVE_SCHEMA_CRITSECT() LeaveCriticalSection(&g_SchemaCritSect)
  25. #define ENTER_SUBSCHEMA_CRITSECT() EnterCriticalSection(&g_SubSchemaCritSect)
  26. #define LEAVE_SUBSCHEMA_CRITSECT() LeaveCriticalSection(&g_SubSchemaCritSect)
  27. #define ENTER_DEFAULTSCHEMA_CRITSECT() EnterCriticalSection(&g_DefaultSchemaCritSect)
  28. #define LEAVE_DEFAULTSCHEMA_CRITSECT() LeaveCriticalSection(&g_DefaultSchemaCritSect)
  29. #define ID_ATTRTYPES 1
  30. #define ID_OBJCLASSES 2
  31. #define ID_DITCONTENTRULES 3
  32. #ifdef WIN95
  33. int ConvertToAscii( WCHAR *pszUnicode, char **pszAscii );
  34. #endif
  35. //
  36. // Constants used to determine what elements of string array to free.
  37. //
  38. const int FREE_ALL = 0;
  39. const int FREE_ARRAY_NOT_ELEMENTS = 1;
  40. const int FREE_ALL_BUT_FIRST = 2;
  41. //
  42. // RFC 2252
  43. //
  44. KWDLIST g_aSchemaKeywordList[] =
  45. {
  46. { TOKEN_NAME, TEXT("NAME") },
  47. { TOKEN_DESC, TEXT("DESC") },
  48. { TOKEN_OBSOLETE, TEXT("OBSOLETE") },
  49. { TOKEN_SUP, TEXT("SUP") },
  50. { TOKEN_EQUALITY, TEXT("EQUALITY") },
  51. { TOKEN_ORDERING, TEXT("ORDERING") },
  52. { TOKEN_SUBSTR, TEXT("SUBSTR") },
  53. { TOKEN_SYNTAX, TEXT("SYNTAX") },
  54. { TOKEN_SINGLE_VALUE, TEXT("SINGLE-VALUE") },
  55. { TOKEN_COLLECTIVE, TEXT("COLLECTIVE") },
  56. { TOKEN_DYNAMIC, TEXT("DYNAMIC") },
  57. { TOKEN_NO_USER_MODIFICATION, TEXT("NO-USER-MODIFICATION") },
  58. { TOKEN_USAGE, TEXT("USAGE") },
  59. { TOKEN_ABSTRACT, TEXT("ABSTRACT") },
  60. { TOKEN_STRUCTURAL, TEXT("STRUCTURAL") },
  61. { TOKEN_AUXILIARY, TEXT("AUXILIARY") },
  62. { TOKEN_MUST, TEXT("MUST") },
  63. { TOKEN_MAY, TEXT("MAY") },
  64. { TOKEN_AUX, TEXT("AUX") },
  65. { TOKEN_NOT, TEXT("NOT") }
  66. // FORM
  67. };
  68. DWORD g_dwSchemaKeywordListSize = sizeof(g_aSchemaKeywordList)/sizeof(KWDLIST);
  69. CRITICAL_SECTION g_SchemaCritSect;
  70. CRITICAL_SECTION g_DefaultSchemaCritSect;
  71. CRITICAL_SECTION g_SubSchemaCritSect;
  72. SCHEMAINFO *g_pSchemaInfoList = NULL; // Link list of cached schema info
  73. SCHEMAINFO *g_pDefaultSchemaInfo = NULL;
  74. //
  75. // Non-AD sd control.
  76. //
  77. #define ADSI_LDAP_OID_SECDESC_OLD L"1.2.840.113556.1.4.416"
  78. typedef struct _subschemalist {
  79. LPWSTR pszLDAPServer;
  80. LPWSTR pszSubSchemaEntry;
  81. BOOL fPagingSupported;
  82. BOOL fSortingSupported;
  83. BOOL fDomScopeSupported;
  84. BOOL fTalkingToAD;
  85. BOOL fTalkingToEnhancedAD;
  86. BOOL fVLVSupported;
  87. BOOL fAttribScopedSupported;
  88. struct _subschemalist *pNext;
  89. BOOL fNoDataGot;
  90. DWORD dwSecDescType;
  91. } SCHEMALIST, *PSCHEMALIST;
  92. //
  93. // The fNoDataReturned will be set for v2 servers that do not
  94. // have a subSchemaSubEntry, this will prevent hitting the server
  95. // multiple times for the same data.
  96. //
  97. typedef SCHEMALIST ROOTDSENODE, *PROOTDSENODE;
  98. PSCHEMALIST gpSubSchemaList = NULL;
  99. static DWORD dwSubSchemaSubEntryCount = 0;
  100. HRESULT
  101. GetSchemaInfoTime(
  102. LPTSTR pszServer,
  103. LPTSTR pszSubSchemaSubEntry,
  104. LPTSTR *ppszTimeReg,
  105. LPTSTR *ppszTimeDS,
  106. CCredentials& Credentials,
  107. DWORD dwPort
  108. );
  109. HRESULT
  110. LdapReadSchemaInfoFromServer(
  111. LPTSTR pszLDAPPath,
  112. LPTSTR pszSubSchemaSubEntry,
  113. LPTSTR pszTimeReg,
  114. LPTSTR pszTimeDS,
  115. SCHEMAINFO **ppSchemaInfo,
  116. CCredentials& Credentials,
  117. DWORD dwPort
  118. );
  119. HRESULT
  120. ReadRootDSENode(
  121. LPWSTR pszLDAPServer,
  122. PROOTDSENODE pRootDSENode,
  123. OUT BOOL * pfBoundOk, // optional, can be NULL
  124. CCredentials& Credentials,
  125. DWORD dwPort
  126. );
  127. HRESULT
  128. LdapReadDefaultSchema(
  129. LPTSTR pszServer,
  130. CCredentials &Credentials,
  131. SCHEMAINFO **ppSchemaInfo
  132. );
  133. HRESULT FillPropertyInfoArray(
  134. LPTSTR *aAttrTypes,
  135. DWORD dwCount,
  136. PROPERTYINFO **paProperties,
  137. DWORD *pnProperties,
  138. SEARCHENTRY **paSearchTable
  139. );
  140. HRESULT FillClassInfoArray(
  141. LPTSTR *aObjectClasses,
  142. DWORD dwCount,
  143. SEARCHENTRY *aPropSearchTable,
  144. DWORD dwSearchTableCount,
  145. CLASSINFO **paClasses,
  146. DWORD *pnClasses,
  147. SEARCHENTRY **paSearchTable
  148. );
  149. HRESULT FillAuxClassInfoArray(
  150. LPTSTR *aDITContentRules,
  151. DWORD dwCount,
  152. SEARCHENTRY *aPropSearchTable,
  153. DWORD dwSearchTableCount,
  154. CLASSINFO *aClasses,
  155. DWORD nClasses,
  156. SEARCHENTRY *aSearchTable
  157. );
  158. HRESULT ProcessClassInfoArray(
  159. CLASSINFO *aClasses,
  160. DWORD nClasses,
  161. SEARCHENTRY *paSearchTable,
  162. BOOL fProcessAUX = FALSE
  163. );
  164. HRESULT ProcessPropertyInfoArray(
  165. PROPERTYINFO *aProperties,
  166. DWORD nProperties,
  167. SEARCHENTRY **paSearchTable
  168. );
  169. DWORD ReadSchemaInfoFromRegistry(
  170. HKEY hKey,
  171. LPWSTR pszServer,
  172. LPTSTR **paValuesAttribTypes,
  173. int *pnCountAttribTypes,
  174. LPTSTR **paValuesObjClasses,
  175. int *pnCountObjClasses,
  176. LPTSTR **paValuesRules,
  177. int *pnCountRules,
  178. LPBYTE *pBuffer
  179. );
  180. DWORD StoreSchemaInfoInRegistry(
  181. HKEY hKey,
  182. LPTSTR pszServer,
  183. LPTSTR pszTime,
  184. LPTSTR *aValuesAttribTypes,
  185. int nCountAttribTypes,
  186. LPTSTR *aValuesObjClasses,
  187. int nCountObjClasses,
  188. LPTSTR *aValuesRules,
  189. int nCountRules,
  190. BOOL fProcessAUX
  191. );
  192. HRESULT
  193. AttributeTypeDescription(
  194. LPTSTR pszAttrType,
  195. PPROPERTYINFO pPropertyInfo,
  196. LPWSTR **pppszNames,
  197. PDWORD pdwNameCount
  198. );
  199. HRESULT
  200. ObjectClassDescription(
  201. LPTSTR pszDescription,
  202. PCLASSINFO pClassInfo,
  203. SEARCHENTRY *aPropSearchTable,
  204. DWORD dwSearchTableCount,
  205. LPWSTR **pppszNames,
  206. PDWORD pdwNameCount
  207. );
  208. HRESULT DITContentRuleDescription(
  209. LPTSTR pszObjectClass,
  210. PCLASSINFO pClassInfo,
  211. SEARCHENTRY *aPropSearchTable,
  212. DWORD dwSearchTableCount
  213. );
  214. //
  215. // Helper routine that adds new elements to the property info array.
  216. //
  217. HRESULT AddNewNamesToPropertyArray(
  218. PROPERTYINFO **ppPropArray,
  219. DWORD dwCurPos,
  220. DWORD dwCount,
  221. LPWSTR *ppszNewNames,
  222. DWORD dwNewNameCount
  223. );
  224. //
  225. // Helper routine that adds new elements to the class info array.
  226. //
  227. HRESULT AddNewNamesToClassArray(
  228. CLASSINFO **ppClassArray,
  229. DWORD dwCurPos,
  230. DWORD dwCount,
  231. LPWSTR *ppszNewNames,
  232. DWORD dwNewNameCount
  233. );
  234. //
  235. // The 3rd param was added to work around bad schema data.
  236. //
  237. HRESULT Oid(
  238. CSchemaLexer * pTokenizer,
  239. LPTSTR *ppszOID,
  240. BOOL fNoGuid = FALSE
  241. );
  242. HRESULT Oids(
  243. CSchemaLexer * pTokenizer,
  244. LPTSTR **pOIDs,
  245. DWORD *pnNumOfOIDs
  246. );
  247. HRESULT PropOids(
  248. CSchemaLexer * pTokenizer,
  249. int **pOIDs,
  250. DWORD *pnNumOfOIDs,
  251. SEARCHENTRY *aPropSearchTable,
  252. DWORD dwSearchTableCount
  253. );
  254. HRESULT DirectoryString(
  255. CSchemaLexer * pTokenizer,
  256. LPTSTR *ppszDirString
  257. );
  258. //
  259. // Returns *pdwCount strings in ppszDirStrings.
  260. //
  261. HRESULT DirectoryStrings(
  262. CSchemaLexer * pTokenizer,
  263. LPTSTR **pppszDirStrings,
  264. PDWORD pdwCount
  265. );
  266. void FreeDirectoryStrings(
  267. LPTSTR *ppszDirStrings,
  268. DWORD dwCount,
  269. DWORD dwElementsToFree= FREE_ALL
  270. );
  271. VOID SortAndRemoveDuplicateOIDs(
  272. int *pOIDs,
  273. DWORD *pnNumOfOIDs
  274. );
  275. int _cdecl searchentrycmp(
  276. const void *s1,
  277. const void *s2
  278. );
  279. long CompareUTCTime(
  280. LPTSTR pszTime1,
  281. LPTSTR pszTime2
  282. );
  283. BOOL
  284. EquivalentServers(
  285. LPWSTR pszTargetServer,
  286. LPWSTR pszSourceServer
  287. );
  288. BOOL
  289. EquivalentUsers(
  290. LPWSTR pszTargetServer,
  291. LPWSTR pszSourceServer
  292. );
  293. DWORD
  294. GetDefaultServer(
  295. DWORD dwPort,
  296. BOOL fVerify,
  297. LPWSTR szDomainDnsName,
  298. LPWSTR szServerName,
  299. BOOL fWriteable
  300. );
  301. //
  302. // Makes a copy of a string array that has NULL as the last element.
  303. // If the copy failed because of lack of memory NULL is returned.
  304. //
  305. LPTSTR *
  306. CopyStringArray(
  307. LPTSTR * ppszStr
  308. )
  309. {
  310. LPTSTR * ppszRetVal = NULL;
  311. DWORD dwCount = 0;
  312. if (!ppszStr) {
  313. BAIL_ON_FAILURE(E_FAIL);
  314. }
  315. //
  316. // Get the count first.
  317. //
  318. while (ppszStr && ppszStr[dwCount]) {
  319. dwCount++;
  320. }
  321. //
  322. // Alloc memory for the array, + 1, is for the NULL string that
  323. // acts as the delimiter for the array.
  324. //
  325. ppszRetVal = (LPTSTR *) AllocADsMem((dwCount+1) * sizeof(LPTSTR));
  326. if (!ppszRetVal) {
  327. BAIL_ON_FAILURE(E_OUTOFMEMORY);
  328. }
  329. for (DWORD dwCtr = 0; dwCtr <= dwCount; dwCtr++) {
  330. if (ppszStr[dwCtr]) {
  331. ppszRetVal[dwCtr] = AllocADsStr(ppszStr[dwCtr]);
  332. if (!ppszRetVal[dwCtr]) {
  333. BAIL_ON_FAILURE(E_OUTOFMEMORY);
  334. }
  335. }
  336. }
  337. return ppszRetVal;
  338. error:
  339. if (ppszRetVal) {
  340. for (DWORD i = 0; i < dwCtr; i++) {
  341. if (ppszRetVal[i]) {
  342. FreeADsStr(ppszRetVal[i]);
  343. }
  344. }
  345. FreeADsMem(ppszRetVal);
  346. ppszRetVal = NULL;
  347. }
  348. //
  349. // Null from this routine means there was a failure.
  350. //
  351. return NULL;
  352. }
  353. VOID
  354. SchemaCleanup(
  355. VOID
  356. )
  357. {
  358. SCHEMAINFO *pList = g_pSchemaInfoList;
  359. while ( pList )
  360. {
  361. SCHEMAINFO *pNext = pList->Next;
  362. delete pList;
  363. pList = pNext;
  364. }
  365. delete g_pDefaultSchemaInfo;
  366. //
  367. // Delete the schema list containing the server infos
  368. //
  369. PSCHEMALIST pSubSchemaList = gpSubSchemaList;
  370. while ( pSubSchemaList )
  371. {
  372. PSCHEMALIST pNext = pSubSchemaList->pNext;
  373. if ( pSubSchemaList->pszLDAPServer )
  374. FreeADsStr( pSubSchemaList->pszLDAPServer );
  375. if ( pSubSchemaList->pszSubSchemaEntry )
  376. FreeADsStr( pSubSchemaList->pszSubSchemaEntry );
  377. FreeADsMem( pSubSchemaList );
  378. pSubSchemaList = pNext;
  379. }
  380. }
  381. HRESULT
  382. LdapGetSchema(
  383. LPTSTR pszLDAPServer,
  384. SCHEMAINFO **ppSchemaInfo,
  385. CCredentials& Credentials,
  386. DWORD dwPort
  387. )
  388. {
  389. HRESULT hr = S_OK;
  390. LPTSTR pszTemp = NULL;
  391. SCHEMAINFO *pList = NULL;
  392. SCHEMAINFO *pPrev = NULL;
  393. LPTSTR pszTimeReg = NULL;
  394. LPTSTR pszTimeDS = NULL;
  395. BOOL fNotCurrent = FALSE;
  396. WCHAR szDomainDnsName[MAX_PATH];
  397. *ppSchemaInfo = NULL;
  398. DWORD nCount =0;
  399. LPWSTR pszSubSchemaEntry = NULL;
  400. BOOL fBoundOk = FALSE; // has once bound to domain okay?
  401. BOOL fReuseSchema = FALSE;
  402. BOOL fTalktoAD = FALSE;
  403. //
  404. // In the case of a serverless path, we want to substitute the name
  405. // of the domain for the serverName. This is because we can get more
  406. // than one file called default.sch if a person logs on from different
  407. // forests on to the same domain.
  408. //
  409. if (!pszLDAPServer) {
  410. WCHAR szServerName[MAX_PATH];
  411. DWORD dwErr;
  412. dwErr = GetDefaultServer(
  413. dwPort,
  414. FALSE, // do not force verify
  415. szDomainDnsName,
  416. szServerName,
  417. !(Credentials.GetAuthFlags() & ADS_READONLY_SERVER)
  418. ? TRUE : FALSE
  419. );
  420. if (dwErr == NO_ERROR) {
  421. //
  422. // Use the domainName returned.
  423. //
  424. pszLDAPServer = szDomainDnsName;
  425. }
  426. }
  427. //
  428. // Check if the server uses default schema and return the schema info
  429. //
  430. hr = Credentials.GetUserName(&pszTemp);
  431. BAIL_IF_ERROR(hr);
  432. ENTER_SCHEMA_CRITSECT();
  433. pList = g_pSchemaInfoList;
  434. pPrev = NULL;
  435. while ( pList )
  436. {
  437. //
  438. // Checking for Schemas can now use NULL and NULL
  439. //
  440. //
  441. // If the server is equivalent, and we've cached it as using
  442. // a default (V2) schema, then we want to immediately return
  443. // that cached schema, UNLESS (1) the server in question
  444. // appeared to be a V3 server when we tried to retrieve the schema
  445. // (i.e., it had a rootDSE with a subschemasubentry), AND (2)
  446. // we're currently using different user credentials then when
  447. // we cached the server schema. This is because we might be going
  448. // against a V3 server that has security restrictions on its schema.
  449. // If we previously tried to read the schema, but didn't have
  450. // sufficient access permissions to do so, we would have defaulted
  451. // to treating it as a v2 schema. Now, if we're using different
  452. // credentials, we try again, in case we now have sufficient
  453. // access permissions to read the schema.
  454. //
  455. if (EquivalentServers(pList->pszServerName, pszLDAPServer)) {
  456. if ( pList->fDefaultSchema &&
  457. !(pList->fAppearsV3 &&
  458. !EquivalentUsers(pszTemp, pList->pszUserName)
  459. )
  460. )
  461. {
  462. *ppSchemaInfo = pList;
  463. (*ppSchemaInfo)->AddRef();
  464. LEAVE_SCHEMA_CRITSECT();
  465. goto cleanup;
  466. }
  467. else if (pList->fDefaultSchema &&
  468. pList->fAppearsV3 &&
  469. !EquivalentUsers(pszTemp, pList->pszUserName))
  470. {
  471. //
  472. // Dump the cached schema in preparation for reading
  473. // it again.
  474. //
  475. if ( pList->IsRefCountZero())
  476. {
  477. if ( pPrev == NULL )
  478. g_pSchemaInfoList = pList->Next;
  479. else
  480. pPrev->Next = pList->Next;
  481. delete pList;
  482. break;
  483. }
  484. }
  485. }
  486. pPrev = pList;
  487. pList = pList->Next;
  488. }
  489. LEAVE_SCHEMA_CRITSECT();
  490. //
  491. // Read the schema path from the root of the DS
  492. //
  493. hr = ReadSubSchemaSubEntry(
  494. pszLDAPServer,
  495. &pszSubSchemaEntry,
  496. &fBoundOk,
  497. Credentials,
  498. dwPort
  499. ) ;
  500. if ( SUCCEEDED(hr)) // pszSubSchemaEntry!=NULL if hr = S_OK. Checked.
  501. {
  502. ENTER_SCHEMA_CRITSECT();
  503. pPrev = NULL;
  504. pList = g_pSchemaInfoList;
  505. while ( pList )
  506. {
  507. hr = ReadServerSupportsIsADControl(pszLDAPServer, &fTalktoAD, Credentials, dwPort);
  508. if (FAILED(hr)) {
  509. //
  510. // Assume it is not AD and continue, there is no
  511. // good reason for this to fail on AD.
  512. //
  513. fTalktoAD = FALSE;
  514. }
  515. if(fTalktoAD) {
  516. // we talking to the server with AD, so then we don't have to compare the servername
  517. fReuseSchema = EquivalentServers(pList->pszSubSchemaSubEntry, pszSubSchemaEntry );
  518. }
  519. else
  520. {
  521. // otherwise, we need to compare the server name
  522. fReuseSchema = EquivalentServers(pList->pszServerName, pszLDAPServer) &&
  523. EquivalentServers(pList->pszSubSchemaSubEntry, pszSubSchemaEntry );
  524. }
  525. if ( fReuseSchema )
  526. {
  527. if ( pList->IsObsolete())
  528. {
  529. hr = GetSchemaInfoTime(
  530. pszLDAPServer,
  531. pszSubSchemaEntry,
  532. &pszTimeReg,
  533. &pszTimeDS,
  534. Credentials,
  535. dwPort );
  536. if ( FAILED(hr))
  537. {
  538. // Cannot get the time, assume the cache is not
  539. // current and read again.
  540. fNotCurrent = TRUE;
  541. break;
  542. }
  543. else
  544. {
  545. //
  546. // If the servers are not the same, then we should
  547. // not comparet the times. This is because
  548. // each server has a ModifyTimeStamp that is not
  549. // based on its update time not that of the domain.
  550. // Note that at this point we know that the
  551. // subSchemaSubEntry is the same.
  552. //
  553. if (!EquivalentServers(
  554. pList->pszServerName,
  555. pszLDAPServer
  556. )
  557. ) {
  558. fNotCurrent = TRUE;
  559. break;
  560. }
  561. // Compare the time to see if we need to read
  562. // the schema info from the file or from the DS
  563. if ( CompareUTCTime( pList->pszTime, pszTimeReg ) >= 0 )
  564. {
  565. if ( CompareUTCTime( pszTimeReg, pszTimeDS ) < 0 )
  566. {
  567. fNotCurrent = TRUE;
  568. break;
  569. }
  570. }
  571. else
  572. {
  573. // The schema in memory is not as current as the
  574. // the one stored in the registry, hence, we
  575. // need to read it anyway.
  576. fNotCurrent = TRUE;
  577. break;
  578. }
  579. }
  580. pList->MakeCurrent();
  581. }
  582. *ppSchemaInfo = pList;
  583. (*ppSchemaInfo)->AddRef();
  584. LEAVE_SCHEMA_CRITSECT();
  585. goto cleanup;
  586. }
  587. pPrev = pList;
  588. pList = pList->Next;
  589. }
  590. if ( fNotCurrent && pList != NULL )
  591. {
  592. if ( pList->IsRefCountZero())
  593. {
  594. SCHEMAINFO *pDelete = pList;
  595. if ( pPrev == NULL )
  596. g_pSchemaInfoList = pDelete->Next;
  597. else
  598. pPrev->Next = pDelete->Next;
  599. delete pDelete;
  600. }
  601. pList = NULL;
  602. }
  603. LEAVE_SCHEMA_CRITSECT();
  604. // pList should be NULL at this point
  605. hr = LdapReadSchemaInfoFromServer(
  606. pszLDAPServer,
  607. pszSubSchemaEntry, // SubSchemaSubEntry
  608. pszTimeReg,
  609. pszTimeDS,
  610. ppSchemaInfo,
  611. Credentials,
  612. dwPort
  613. );
  614. if (SUCCEEDED(hr)) {
  615. ENTER_SCHEMA_CRITSECT();
  616. (*ppSchemaInfo)->Next = g_pSchemaInfoList;
  617. g_pSchemaInfoList = *ppSchemaInfo;
  618. (*ppSchemaInfo)->AddRef();
  619. LEAVE_SCHEMA_CRITSECT();
  620. }
  621. else {
  622. //
  623. // There was some problem in reading from the DS. If it was
  624. // because of some error like the attributes were not
  625. // obtained or were not of the proper form, we will fall
  626. // back to the default schema
  627. //
  628. hr = LdapReadDefaultSchema(pszLDAPServer, Credentials, ppSchemaInfo);
  629. BAIL_IF_ERROR(hr);
  630. //
  631. // We leave fAppearsV3 == TRUE because this server has a
  632. // subschemasubentry --- it's just that we can't read the
  633. // schema (e.g., maybe we don't have permission)
  634. //
  635. ENTER_SCHEMA_CRITSECT();
  636. (*ppSchemaInfo)->Next = g_pSchemaInfoList;
  637. g_pSchemaInfoList = *ppSchemaInfo;
  638. (*ppSchemaInfo)->AddRef();
  639. LEAVE_SCHEMA_CRITSECT();
  640. }
  641. } // end of if read of subSchemaSubEntry succeeded
  642. else if ( fBoundOk )
  643. {
  644. //
  645. // If we cannot get subschemasubentry, use default schema if
  646. // fBoundOk; that is, we have at least
  647. // once bound to the domain successfully before.
  648. //
  649. hr = LdapReadDefaultSchema( pszLDAPServer, Credentials, ppSchemaInfo );
  650. BAIL_IF_ERROR(hr);
  651. (*ppSchemaInfo)->fAppearsV3 = FALSE;
  652. ENTER_SCHEMA_CRITSECT();
  653. (*ppSchemaInfo)->Next = g_pSchemaInfoList;
  654. g_pSchemaInfoList = *ppSchemaInfo;
  655. (*ppSchemaInfo)->AddRef();
  656. LEAVE_SCHEMA_CRITSECT();
  657. }
  658. else
  659. {
  660. //
  661. // we cannot read subschemasubentry, but we are not using
  662. // default schema since we have no indication that the
  663. // we had ever bound to the domain before
  664. //
  665. if ( SUCCEEDED(hr)) // i.e. we could not read the schema
  666. {
  667. hr = E_ADS_BAD_PATHNAME;
  668. }
  669. BAIL_IF_ERROR(hr);
  670. }
  671. cleanup:
  672. if (pszSubSchemaEntry) {
  673. FreeADsStr(pszSubSchemaEntry);
  674. }
  675. if ( pszTimeReg )
  676. FreeADsMem( pszTimeReg );
  677. if ( pszTimeDS )
  678. FreeADsMem( pszTimeDS );
  679. if ( pszTemp )
  680. FreeADsStr( pszTemp );
  681. RRETURN(hr);
  682. }
  683. HRESULT
  684. LdapRemoveSchemaInfoOnServer(
  685. LPTSTR pszLDAPPath,
  686. CCredentials& Credentials,
  687. DWORD dwPort,
  688. BOOL fForce
  689. )
  690. {
  691. HRESULT hr = S_OK;
  692. SCHEMAINFO *pList = NULL;
  693. LPWSTR pszSubSchemaSubEntry = NULL;
  694. BOOL fBoundOk = FALSE;
  695. //
  696. // Read the subschemaSubEntry only once.
  697. //
  698. hr = ReadSubSchemaSubEntry(
  699. pszLDAPPath,
  700. &pszSubSchemaSubEntry,
  701. &fBoundOk,
  702. Credentials,
  703. dwPort
  704. ) ;
  705. //
  706. // If we cannot read the subSchemaSubEntry it is not a
  707. // V3 server and we cannot refresh.
  708. //
  709. BAIL_ON_FAILURE(hr);
  710. ENTER_SCHEMA_CRITSECT();
  711. pList = g_pSchemaInfoList;
  712. while ( pList )
  713. {
  714. //
  715. // Both NULL and NULL and also check for the servers
  716. //
  717. if (!pList->pszServerName && !pszLDAPPath) {
  718. pList->MakeObsolete();
  719. if (fForce) {
  720. //
  721. // Will reset time to something ancient so we
  722. // will always pick up the schema from server.
  723. //
  724. LPWSTR pszTempTime = NULL;
  725. if(pList->pszTime)
  726. {
  727. pszTempTime = AllocADsStr(L"19800719000000.0Z");
  728. if (pszTempTime) {
  729. FreeADsStr(pList->pszTime);
  730. pList->pszTime = pszTempTime;
  731. }
  732. else
  733. {
  734. hr = E_OUTOFMEMORY;
  735. break;
  736. }
  737. }
  738. }
  739. } else {
  740. //
  741. // The match at this point has to be made based on the
  742. // subschemaSubEntry and not on the server names.
  743. //
  744. if (EquivalentServers(
  745. pList->pszSubSchemaSubEntry,
  746. pszSubSchemaSubEntry
  747. )
  748. )
  749. {
  750. pList->MakeObsolete();
  751. if (fForce) {
  752. //
  753. // Will reset time to something ancient so we
  754. // will always pick up the schema from server.
  755. //
  756. LPWSTR pszTempTime = NULL;
  757. if(pList->pszTime)
  758. {
  759. pszTempTime = AllocADsStr(L"19800719000000.0Z");
  760. if (pszTempTime) {
  761. FreeADsStr(pList->pszTime);
  762. pList->pszTime = pszTempTime;
  763. }
  764. else
  765. {
  766. hr = E_OUTOFMEMORY;
  767. break;
  768. }
  769. }
  770. }
  771. }
  772. } // the server name is not NULL
  773. pList = pList->Next;
  774. }
  775. LEAVE_SCHEMA_CRITSECT();
  776. error :
  777. if (pszSubSchemaSubEntry) {
  778. FreeADsStr(pszSubSchemaSubEntry);
  779. }
  780. RRETURN(hr);
  781. }
  782. HRESULT
  783. GetSchemaInfoTime(
  784. LPTSTR pszLDAPServer,
  785. LPTSTR pszSubSchemaSubEntry,
  786. LPTSTR *ppszTimeReg,
  787. LPTSTR *ppszTimeDS,
  788. CCredentials& Credentials,
  789. DWORD dwPort
  790. )
  791. {
  792. HRESULT hr = S_OK;
  793. DWORD dwStatus = NO_ERROR;
  794. LPTSTR pszLDAPPath = NULL;
  795. LPTSTR pszRegPath = NULL;
  796. LPTSTR *aValues = NULL;
  797. int nCount = 0;
  798. TCHAR szTimeReg[64];
  799. HKEY hKey = NULL;
  800. DWORD dwLength;
  801. DWORD dwType;
  802. //
  803. // Read the schema timestamp on the DS server
  804. //
  805. hr = LdapReadAttribute2(
  806. pszLDAPServer,
  807. NULL,
  808. pszSubSchemaSubEntry,
  809. TEXT("modifyTimeStamp"),
  810. &aValues,
  811. &nCount,
  812. Credentials,
  813. dwPort,
  814. L"(objectClass=subschema)"
  815. );
  816. if (nCount==0) {
  817. //
  818. // cannot get to time stamp or get to a time stamp with no values:
  819. // both treat as E_FAIL
  820. //
  821. hr = E_FAIL;
  822. }
  823. BAIL_IF_ERROR(hr);
  824. ADsAssert( nCount == 1 );
  825. *ppszTimeDS = AllocADsStr( aValues[0] );
  826. LdapValueFree( aValues );
  827. if ( *ppszTimeDS == NULL )
  828. {
  829. hr = E_OUTOFMEMORY;
  830. BAIL_IF_ERROR(hr);
  831. }
  832. //
  833. // See if we can find the schema info in the registry
  834. //
  835. pszRegPath = (LPTSTR) AllocADsMem( (_tcslen(ADSI_LDAP_KEY) +
  836. _tcslen(pszSubSchemaSubEntry) +
  837. 2 ) * sizeof(TCHAR)); // includes "\\"
  838. if ( pszRegPath == NULL )
  839. {
  840. hr = E_OUTOFMEMORY;
  841. BAIL_IF_ERROR(hr);
  842. }
  843. _tcscpy( pszRegPath, ADSI_LDAP_KEY );
  844. _tcscat( pszRegPath, TEXT("\\"));
  845. _tcscat( pszRegPath, pszSubSchemaSubEntry );
  846. dwStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  847. pszRegPath,
  848. 0,
  849. KEY_READ,
  850. &hKey
  851. );
  852. if ( dwStatus != NO_ERROR )
  853. {
  854. hr = HRESULT_FROM_WIN32(dwStatus);
  855. BAIL_IF_ERROR(hr);
  856. }
  857. //
  858. // Read the time stamp of the schema in registry.
  859. //
  860. dwLength = sizeof(szTimeReg);
  861. dwStatus = RegQueryValueEx( hKey,
  862. SCHEMA_TIME,
  863. NULL,
  864. &dwType,
  865. (LPBYTE) szTimeReg,
  866. &dwLength );
  867. if ( dwStatus )
  868. {
  869. hr = HRESULT_FROM_WIN32(dwStatus);
  870. BAIL_IF_ERROR(hr);
  871. }
  872. else
  873. {
  874. *ppszTimeReg = AllocADsStr( szTimeReg );
  875. if ( *ppszTimeReg == NULL )
  876. {
  877. hr = E_OUTOFMEMORY;
  878. BAIL_IF_ERROR(hr);
  879. }
  880. }
  881. cleanup:
  882. if ( hKey )
  883. RegCloseKey( hKey );
  884. if ( pszLDAPPath != NULL )
  885. FreeADsStr( pszLDAPPath );
  886. if ( pszRegPath != NULL )
  887. FreeADsStr( pszRegPath );
  888. if ( FAILED(hr))
  889. {
  890. if ( *ppszTimeDS )
  891. {
  892. FreeADsMem( *ppszTimeDS );
  893. *ppszTimeDS = NULL;
  894. }
  895. if ( *ppszTimeReg )
  896. {
  897. FreeADsMem( *ppszTimeReg );
  898. *ppszTimeReg = NULL;
  899. }
  900. }
  901. RRETURN(hr);
  902. }
  903. HRESULT
  904. LdapReadSchemaInfoFromServer(
  905. LPTSTR pszLDAPServer,
  906. LPTSTR pszSubSchemaSubEntry,
  907. LPTSTR pszTimeReg,
  908. LPTSTR pszTimeDS,
  909. SCHEMAINFO **ppSchemaInfo,
  910. CCredentials& Credentials,
  911. DWORD dwPort
  912. )
  913. {
  914. HRESULT hr = S_OK;
  915. DWORD dwStatus = NO_ERROR;
  916. LPTSTR pszRegPath = NULL;
  917. SCHEMAINFO *pSchemaInfo = NULL;
  918. LPTSTR *aValues = NULL;
  919. int nCount = 0;
  920. TCHAR szTimeReg[64];
  921. LPWSTR aStrings[4] = { L"attributeTypes",
  922. L"objectClasses",
  923. L"ditContentRules",
  924. NULL
  925. };
  926. LPWSTR szNTFilter = L"objectClass=*";
  927. LPWSTR szGenericFilter = L"objectClass=subSchema";
  928. BOOL fNTDS = FALSE;
  929. DWORD dwSecDescType = 0;
  930. LPTSTR *aValuesAttribTypes = NULL;
  931. int nCountAttribTypes = 0;
  932. LPTSTR *aValuesObjClasses = NULL;
  933. int nCountObjClasses = 0;
  934. LPTSTR *aValuesRules = NULL;
  935. int nCountRules = 0;
  936. LPBYTE Buffer = NULL;
  937. HKEY hKeySchema = NULL;
  938. HKEY hKey = NULL;
  939. DWORD dwDisposition;
  940. BOOL fReadFromDS = TRUE;
  941. BOOL fProcessAUX = FALSE;
  942. DWORD dwRegPathLen = 0;
  943. *ppSchemaInfo = NULL;
  944. DWORD dwRegAUXType = REG_DWORD;
  945. DWORD dwRegProcessAUX = 0;
  946. DWORD dwRegLength = sizeof(dwRegProcessAUX);
  947. //
  948. // Allocate an entry for the schema info that we are going to read
  949. //
  950. #if DBG
  951. static BOOL fSchemaRead = FALSE;
  952. static BOOL fGoSchemaLess = FALSE;
  953. WCHAR pszRegPathDbg[MAX_PATH];
  954. DWORD dwType = 0;
  955. DWORD dwRetVal = 0;
  956. DWORD dwLength = 0;
  957. if (!fSchemaRead) {
  958. _tcscpy( pszRegPathDbg, ADSI_LDAP_KEY );
  959. _tcscat( pszRegPathDbg, TEXT("\\"));
  960. _tcscat( pszRegPathDbg, TEXT("DBGSchema"));
  961. //DebugDisabled
  962. // If DBG, try and read the schema key and return
  963. // value if that is set to 1.
  964. dwStatus = RegOpenKeyEx(
  965. HKEY_LOCAL_MACHINE,
  966. pszRegPathDbg,
  967. 0,
  968. KEY_READ,
  969. &hKeySchema
  970. );
  971. if (dwStatus != NO_ERROR) {
  972. // Do not want to keep coming back to this.
  973. fSchemaRead = TRUE;
  974. } else {
  975. dwLength = sizeof(DWORD);
  976. // Read the value of the DWORD DebugDisabled
  977. dwStatus = RegQueryValueEx(
  978. hKeySchema,
  979. L"DebugDisabled",
  980. 0,
  981. &dwType,
  982. (LPBYTE) &dwRetVal,
  983. &dwLength
  984. );
  985. if (dwStatus != NO_ERROR) {
  986. fSchemaRead = TRUE;
  987. } else {
  988. // Look at the value and proceed
  989. if (dwRetVal == 0) {
  990. fGoSchemaLess = TRUE;
  991. hr = E_FAIL;
  992. }
  993. } // else - we were able to read the DebugDisabled key
  994. } // else - we were able to open the key
  995. } // if fSchemaRead
  996. if ( hKeySchema )
  997. RegCloseKey( hKeySchema );
  998. // hr will be set only if we have schema disabled.
  999. // Note that hr is initialised to S_OK so default case
  1000. // will fall through
  1001. BAIL_IF_ERROR(hr);
  1002. #endif
  1003. pSchemaInfo = new SCHEMAINFO;
  1004. if ( pSchemaInfo == NULL )
  1005. {
  1006. hr = E_OUTOFMEMORY;
  1007. BAIL_IF_ERROR(hr);
  1008. }
  1009. memset(pSchemaInfo, 0, sizeof(SCHEMAINFO));
  1010. //
  1011. // Store the server name
  1012. //
  1013. if (pszLDAPServer) {
  1014. pSchemaInfo->pszServerName = AllocADsStr( pszLDAPServer );
  1015. if ( pSchemaInfo->pszServerName == NULL )
  1016. {
  1017. hr = E_OUTOFMEMORY;
  1018. BAIL_IF_ERROR(hr);
  1019. }
  1020. }
  1021. //
  1022. // Store the name of the user under whose credentials
  1023. // we're reading the schema
  1024. //
  1025. hr = Credentials.GetUserName(&(pSchemaInfo->pszUserName));
  1026. BAIL_IF_ERROR(hr);
  1027. //
  1028. // Store the subSchemaSubEntry path
  1029. //
  1030. pSchemaInfo->pszSubSchemaSubEntry = AllocADsStr( pszSubSchemaSubEntry );
  1031. if ( pSchemaInfo->pszSubSchemaSubEntry == NULL )
  1032. {
  1033. hr = E_OUTOFMEMORY;
  1034. BAIL_IF_ERROR(hr);
  1035. }
  1036. //
  1037. // Try and see if this is NTDS or not to optimize schema calls.
  1038. // This is very likely to be satisfied from our cache as we would
  1039. // have already read the RootDSE at this point.
  1040. //
  1041. hr = ReadSecurityDescriptorControlType(
  1042. pszLDAPServer,
  1043. &dwSecDescType,
  1044. Credentials,
  1045. dwPort
  1046. );
  1047. if (SUCCEEDED(hr) && (dwSecDescType == ADSI_LDAPC_SECDESC_NT))
  1048. fNTDS = TRUE;
  1049. if ( pszTimeDS == NULL )
  1050. {
  1051. hr = LdapReadAttribute2(
  1052. pszLDAPServer,
  1053. NULL,
  1054. pszSubSchemaSubEntry,
  1055. TEXT("modifyTimeStamp"),
  1056. &aValues,
  1057. &nCount,
  1058. Credentials,
  1059. dwPort,
  1060. fNTDS ? szNTFilter : szGenericFilter
  1061. );
  1062. if (FAILED(hr) || nCount==0)
  1063. {
  1064. //
  1065. // cannot read modifyTimeStamp or modifyTimeStamp has no values:
  1066. // - treat as same
  1067. //
  1068. hr = S_OK;
  1069. }
  1070. else
  1071. {
  1072. ADsAssert( nCount == 1 );
  1073. pSchemaInfo->pszTime = AllocADsStr( aValues[0] );
  1074. LdapValueFree( aValues );
  1075. if ( pSchemaInfo->pszTime == NULL )
  1076. {
  1077. hr = E_OUTOFMEMORY;
  1078. BAIL_IF_ERROR(hr);
  1079. }
  1080. }
  1081. }
  1082. else
  1083. {
  1084. pSchemaInfo->pszTime = AllocADsStr( pszTimeDS );
  1085. if ( pSchemaInfo->pszTime == NULL )
  1086. {
  1087. hr = E_OUTOFMEMORY;
  1088. BAIL_IF_ERROR(hr);
  1089. }
  1090. }
  1091. //
  1092. // See if we can find the schema info in the registry
  1093. //
  1094. dwRegPathLen = _tcslen(ADSI_LDAP_KEY)
  1095. + _tcslen(pszSubSchemaSubEntry)
  1096. + (pszLDAPServer ? _tcslen(pszLDAPServer) : 0)
  1097. + 3; // includes "\\" and . for serverName
  1098. pszRegPath = (LPTSTR) AllocADsMem( dwRegPathLen * sizeof(TCHAR));
  1099. if ( pszRegPath == NULL )
  1100. {
  1101. hr = E_OUTOFMEMORY;
  1102. BAIL_IF_ERROR(hr);
  1103. }
  1104. _tcscpy( pszRegPath, ADSI_LDAP_KEY );
  1105. _tcscat( pszRegPath, TEXT("\\"));
  1106. _tcscat( pszRegPath, pszSubSchemaSubEntry );
  1107. //
  1108. // If the server is not NTDS, and it has the subSchemaSubEntry cn=Schema,
  1109. // to avoid schema key conflicts, we will add .ServerName to the key.
  1110. //
  1111. if (!fNTDS
  1112. && pszSubSchemaSubEntry
  1113. && pszLDAPServer // should alwasy be true
  1114. && !_tcsicmp(pszSubSchemaSubEntry, TEXT("cn=Schema"))
  1115. ) {
  1116. _tcscat( pszRegPath, TEXT("."));
  1117. _tcscat( pszRegPath, pszLDAPServer);
  1118. }
  1119. dwStatus = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
  1120. pszRegPath,
  1121. 0,
  1122. TEXT(""),
  1123. REG_OPTION_NON_VOLATILE, // or volatile
  1124. KEY_READ | KEY_WRITE,
  1125. NULL,
  1126. &hKey,
  1127. &dwDisposition
  1128. );
  1129. if (dwStatus == NO_ERROR) {
  1130. if ( ( dwDisposition == REG_OPENED_EXISTING_KEY )
  1131. && ( pSchemaInfo->pszTime != NULL )
  1132. && ( pszTimeReg == NULL )
  1133. )
  1134. {
  1135. //
  1136. // Read the time stamp of the schema in cache and the time stamp
  1137. // of the schema on the server. If the time stamp on the server is
  1138. // newer, then we need to read the info from the server. Else
  1139. // the info in the cache is current and hence don't need to read
  1140. // it again.
  1141. //
  1142. DWORD dwLength = sizeof(szTimeReg);
  1143. DWORD dwType;
  1144. dwStatus = RegQueryValueEx( hKey,
  1145. SCHEMA_TIME,
  1146. NULL,
  1147. &dwType,
  1148. (LPBYTE) szTimeReg,
  1149. &dwLength );
  1150. if ( dwStatus )
  1151. {
  1152. dwStatus = NO_ERROR;
  1153. }
  1154. else
  1155. {
  1156. // Compare the two time
  1157. if ( CompareUTCTime( szTimeReg, pSchemaInfo->pszTime ) >= 0 )
  1158. fReadFromDS = FALSE;
  1159. }
  1160. }
  1161. else if ( ( pSchemaInfo->pszTime != NULL ) && ( pszTimeReg != NULL ))
  1162. {
  1163. if ( CompareUTCTime( pszTimeReg, pSchemaInfo->pszTime ) >= 0 )
  1164. fReadFromDS = FALSE;
  1165. }
  1166. }else {
  1167. fReadFromDS = TRUE;
  1168. }
  1169. if ( !fReadFromDS )
  1170. {
  1171. //
  1172. // Read from registry, if we failed to read from the registry,
  1173. // then read it from the DS.
  1174. //
  1175. //
  1176. // We can av while reading bad info from a file
  1177. // or while processing it
  1178. //
  1179. __try {
  1180. dwStatus = ReadSchemaInfoFromRegistry(
  1181. hKey,
  1182. pszLDAPServer,
  1183. &aValuesAttribTypes,
  1184. &nCountAttribTypes,
  1185. &aValuesObjClasses,
  1186. &nCountObjClasses,
  1187. &aValuesRules,
  1188. &nCountRules,
  1189. &Buffer
  1190. );
  1191. if ( dwStatus == NO_ERROR)
  1192. {
  1193. //
  1194. // At this stage we need to try and process the info
  1195. // we got from the file. There is always a chance that
  1196. // the read was successful but the schema data is bad
  1197. //
  1198. //
  1199. // First we need to read from the registry to find whether we need to process
  1200. // AUX class or not.
  1201. //
  1202. dwStatus = RegQueryValueExW( hKey,
  1203. SCHEMA_PROCESSAUX,
  1204. NULL,
  1205. &dwRegAUXType,
  1206. (LPBYTE) &dwRegProcessAUX,
  1207. &dwRegLength);
  1208. if(ERROR_SUCCESS == dwStatus) {
  1209. fProcessAUX = (BOOL) dwRegProcessAUX;
  1210. hr = ProcessSchemaInfo(
  1211. pSchemaInfo,
  1212. aValuesAttribTypes,
  1213. nCountAttribTypes,
  1214. aValuesObjClasses,
  1215. nCountObjClasses,
  1216. aValuesRules,
  1217. nCountRules,
  1218. fProcessAUX
  1219. );
  1220. }
  1221. }
  1222. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1223. dwStatus = GetExceptionCode();
  1224. if (dwStatus != EXCEPTION_ACCESS_VIOLATION) {
  1225. ADsDebugOut((DEB_ERROR, "Processing Schema Info:Unknown Exception %d\n", dwStatus));
  1226. }
  1227. hr = E_FAIL;
  1228. } // end of exception handler
  1229. if (FAILED(hr) || dwStatus) {
  1230. //
  1231. // We can read the schema from the ds and upgrade our
  1232. // local copy to get rid of the bad file
  1233. //
  1234. fReadFromDS = TRUE;
  1235. //
  1236. // Need to cleanup here so that we wont leak mem.
  1237. //
  1238. if ( aValuesAttribTypes ){
  1239. FreeADsMem( aValuesAttribTypes );
  1240. aValuesAttribTypes = NULL;
  1241. }
  1242. if ( aValuesObjClasses ) {
  1243. FreeADsMem( aValuesObjClasses );
  1244. aValuesObjClasses = NULL;
  1245. }
  1246. if ( aValuesRules ) {
  1247. FreeADsMem( aValuesRules );
  1248. aValuesRules = NULL;
  1249. }
  1250. if ( Buffer ) {
  1251. FreeADsMem( Buffer );
  1252. Buffer = NULL;
  1253. }
  1254. hr = E_FAIL;
  1255. fReadFromDS = TRUE;
  1256. }
  1257. } // if !fReadFromDS
  1258. if ( fReadFromDS )
  1259. {
  1260. //
  1261. // At this point, the info in the DS is newer or we have failed
  1262. // to read the info from the registry, hence we need to read
  1263. // from the DS and then store it in the registry.
  1264. //
  1265. //
  1266. // As per the LDAP spec if the server does not know about
  1267. // an attribute then it will ignore the attribute. So it should
  1268. // be ok to ask for the ditContentRules even though the server
  1269. // may not know about them.
  1270. //
  1271. hr = HelperReadLDAPSchemaInfo(
  1272. pszLDAPServer,
  1273. pszSubSchemaSubEntry,
  1274. aStrings,
  1275. fNTDS ? szNTFilter : szGenericFilter,
  1276. &aValuesAttribTypes,
  1277. &aValuesObjClasses,
  1278. &aValuesRules,
  1279. &nCountAttribTypes,
  1280. &nCountObjClasses,
  1281. &nCountRules,
  1282. Credentials,
  1283. dwPort);
  1284. BAIL_IF_ERROR(hr);
  1285. if (nCountAttribTypes == 0 || nCountObjClasses == 0) {
  1286. BAIL_IF_ERROR(hr = E_FAIL);
  1287. }
  1288. //
  1289. // We need to know if we need to process the aux classes
  1290. // or not at this stage. If the server is enhanced AD (build 2220+),
  1291. // we should not. Also if it is anything other than AD on Win2k we should
  1292. // not as we will end up interpreting the schema incorrectly.
  1293. //
  1294. BOOL fLaterThanAD, fAD;
  1295. hr = ReadServerSupportsIsEnhancedAD(
  1296. pszLDAPServer,
  1297. &fLaterThanAD,
  1298. &fAD,
  1299. Credentials,
  1300. dwPort
  1301. );
  1302. if (FAILED(hr)) {
  1303. //
  1304. // We will not process the aux classes.
  1305. //
  1306. fProcessAUX = FALSE;
  1307. }
  1308. if (fLaterThanAD) {
  1309. fProcessAUX = FALSE;
  1310. }
  1311. else if (!fLaterThanAD && fAD) {
  1312. fProcessAUX = TRUE;
  1313. }
  1314. //
  1315. // This is not expected to AV as this is info from the
  1316. // server that is why it is not in a try except block
  1317. //
  1318. hr = ProcessSchemaInfo(
  1319. pSchemaInfo,
  1320. aValuesAttribTypes,
  1321. nCountAttribTypes,
  1322. aValuesObjClasses,
  1323. nCountObjClasses,
  1324. aValuesRules,
  1325. nCountRules,
  1326. fProcessAUX
  1327. );
  1328. BAIL_IF_ERROR(hr);
  1329. } // if fReadFromDS
  1330. //
  1331. // Store all the info in the registry only if the time stamp
  1332. // is present and we have read just read it from the server.
  1333. // Ignore the error since if we failed to store it, we can
  1334. // still read it from the DS.
  1335. //
  1336. if ( fReadFromDS && pSchemaInfo->pszTime )
  1337. {
  1338. StoreSchemaInfoInRegistry( hKey,
  1339. pszLDAPServer,
  1340. pSchemaInfo->pszTime,
  1341. aValuesAttribTypes,
  1342. nCountAttribTypes,
  1343. aValuesObjClasses,
  1344. nCountObjClasses,
  1345. aValuesRules,
  1346. nCountRules,
  1347. fProcessAUX);
  1348. }
  1349. *ppSchemaInfo = pSchemaInfo;
  1350. cleanup:
  1351. if ( fReadFromDS )
  1352. {
  1353. if ( aValuesAttribTypes )
  1354. LdapValueFree( aValuesAttribTypes );
  1355. if ( aValuesObjClasses )
  1356. LdapValueFree( aValuesObjClasses );
  1357. if ( aValuesRules )
  1358. LdapValueFree( aValuesRules );
  1359. }
  1360. else
  1361. {
  1362. if ( aValuesAttribTypes )
  1363. FreeADsMem( aValuesAttribTypes );
  1364. if ( aValuesObjClasses )
  1365. FreeADsMem( aValuesObjClasses );
  1366. if ( aValuesRules )
  1367. FreeADsMem( aValuesRules );
  1368. if ( Buffer )
  1369. FreeADsMem( Buffer );
  1370. }
  1371. if ( hKey )
  1372. RegCloseKey( hKey );
  1373. if ( pszRegPath != NULL )
  1374. FreeADsStr( pszRegPath );
  1375. if ( FAILED(hr) && pSchemaInfo )
  1376. delete pSchemaInfo;
  1377. RRETURN(hr);
  1378. }
  1379. HRESULT
  1380. ProcessSchemaInfo(
  1381. SCHEMAINFO *pSchemaInfo,
  1382. LPTSTR *aValuesAttribTypes,
  1383. DWORD dwAttribCount,
  1384. LPTSTR *aValuesObjClasses,
  1385. DWORD dwObjClassesCount,
  1386. LPTSTR *aValuesRules,
  1387. DWORD dwRulesCount,
  1388. BOOL fProcessAUX
  1389. )
  1390. {
  1391. HRESULT hr = S_OK;
  1392. hr = FillPropertyInfoArray(
  1393. aValuesAttribTypes,
  1394. dwAttribCount,
  1395. &(pSchemaInfo->aProperties),
  1396. &(pSchemaInfo->nNumOfProperties),
  1397. &(pSchemaInfo->aPropertiesSearchTable)
  1398. );
  1399. BAIL_IF_ERROR(hr);
  1400. hr = FillClassInfoArray(
  1401. aValuesObjClasses,
  1402. dwObjClassesCount,
  1403. pSchemaInfo->aPropertiesSearchTable,
  1404. pSchemaInfo->nNumOfProperties * 2,
  1405. &(pSchemaInfo->aClasses),
  1406. &(pSchemaInfo->nNumOfClasses),
  1407. &(pSchemaInfo->aClassesSearchTable)
  1408. );
  1409. BAIL_IF_ERROR(hr);
  1410. if ( aValuesRules )
  1411. {
  1412. hr = FillAuxClassInfoArray(
  1413. aValuesRules,
  1414. dwRulesCount,
  1415. pSchemaInfo->aPropertiesSearchTable,
  1416. pSchemaInfo->nNumOfProperties * 2,
  1417. pSchemaInfo->aClasses,
  1418. pSchemaInfo->nNumOfClasses,
  1419. pSchemaInfo->aClassesSearchTable
  1420. );
  1421. BAIL_IF_ERROR(hr);
  1422. }
  1423. //
  1424. // fProcssAUX tells us if we need to add the list of must
  1425. // contain on each of the classes in the AUX list to the appopriate
  1426. // classes list. Say :
  1427. // 1.2.3.4 NAME 'OrganizationalUnit' AUX ($Class1 $CLASS2) MUST (List)
  1428. // May (List). Then if the flag is true, we will add the Must and May
  1429. // of class1 and class2 to the must and may of class OrganizationalUnit
  1430. // (the must and may list is always processed - they are lists
  1431. // of attributes).
  1432. //
  1433. hr = ProcessClassInfoArray(
  1434. pSchemaInfo->aClasses,
  1435. pSchemaInfo->nNumOfClasses,
  1436. pSchemaInfo->aClassesSearchTable,
  1437. fProcessAUX
  1438. );
  1439. BAIL_IF_ERROR(hr);
  1440. cleanup :
  1441. //
  1442. // Nothing to do for now
  1443. //
  1444. RRETURN(hr);
  1445. }
  1446. //
  1447. // Helper to read the schema information from subSchemaSubEntry
  1448. //
  1449. HRESULT
  1450. HelperReadLDAPSchemaInfo(
  1451. LPWSTR pszLDAPServer,
  1452. LPWSTR pszSubSchemaSubEntry,
  1453. LPWSTR szAttributes[],
  1454. LPWSTR pszFilter,
  1455. LPTSTR **aValuesAttribTypes,
  1456. LPTSTR **aValuesObjClasses,
  1457. LPTSTR **aValuesRules,
  1458. int *nCountAttributes,
  1459. int *nCountObjClasses,
  1460. int *nCountRules,
  1461. CCredentials& Credentials,
  1462. DWORD dwPort
  1463. )
  1464. {
  1465. HRESULT hr = S_OK;
  1466. ADS_LDP *ld = NULL;
  1467. LDAPMessage *res = NULL;
  1468. LDAPMessage *e = NULL;
  1469. hr = LdapOpenObject2(
  1470. pszLDAPServer,
  1471. NULL,
  1472. pszSubSchemaSubEntry,
  1473. &ld,
  1474. Credentials,
  1475. dwPort
  1476. );
  1477. BAIL_ON_FAILURE(hr);
  1478. ADsAssert(ld && ld->LdapHandle);
  1479. hr = LdapSearchS(
  1480. ld,
  1481. pszSubSchemaSubEntry,
  1482. LDAP_SCOPE_BASE,
  1483. pszFilter,
  1484. szAttributes,
  1485. 0,
  1486. &res
  1487. );
  1488. BAIL_ON_FAILURE(hr);
  1489. BAIL_ON_FAILURE((hr = LdapFirstEntry(ld, res, &e)));
  1490. hr = LdapGetValues(
  1491. ld,
  1492. e,
  1493. szAttributes[0],
  1494. aValuesAttribTypes,
  1495. nCountAttributes
  1496. );
  1497. BAIL_ON_FAILURE(hr);
  1498. hr = LdapGetValues(
  1499. ld,
  1500. e,
  1501. szAttributes[1],
  1502. aValuesObjClasses,
  1503. nCountObjClasses
  1504. );
  1505. BAIL_ON_FAILURE(hr);
  1506. hr = LdapGetValues(
  1507. ld,
  1508. e,
  1509. szAttributes[2],
  1510. aValuesRules,
  1511. nCountRules
  1512. );
  1513. if (FAILED(hr)) {
  1514. //
  1515. // This is non critical
  1516. //
  1517. *aValuesRules = NULL;
  1518. nCountRules = 0;
  1519. hr = S_OK;
  1520. }
  1521. error:
  1522. if (res) {
  1523. LdapMsgFree(res);
  1524. }
  1525. if (ld) {
  1526. LdapCloseObject(ld);
  1527. }
  1528. RRETURN(hr);
  1529. }
  1530. HRESULT
  1531. LdapReadDefaultSchema(
  1532. LPTSTR pszServer,
  1533. CCredentials &Credentials,
  1534. SCHEMAINFO **ppSchemaInfo
  1535. )
  1536. {
  1537. HRESULT hr = S_OK;
  1538. SCHEMAINFO *pSchemaInfo = NULL;
  1539. *ppSchemaInfo = NULL;
  1540. ENTER_DEFAULTSCHEMA_CRITSECT();
  1541. if ( g_pDefaultSchemaInfo == NULL )
  1542. {
  1543. g_pDefaultSchemaInfo = new SCHEMAINFO;
  1544. if ( g_pDefaultSchemaInfo == NULL )
  1545. {
  1546. LEAVE_DEFAULTSCHEMA_CRITSECT();
  1547. hr = E_OUTOFMEMORY;
  1548. BAIL_IF_ERROR(hr);
  1549. }
  1550. hr = FillPropertyInfoArray( g_aDefaultAttributeTypes,
  1551. g_cDefaultAttributeTypes,
  1552. &(g_pDefaultSchemaInfo->aProperties),
  1553. &(g_pDefaultSchemaInfo->nNumOfProperties),
  1554. &(g_pDefaultSchemaInfo->aPropertiesSearchTable));
  1555. //
  1556. // Now read the object classes from the schema
  1557. //
  1558. if ( SUCCEEDED(hr))
  1559. {
  1560. hr = FillClassInfoArray( g_aDefaultObjectClasses,
  1561. g_cDefaultObjectClasses,
  1562. g_pDefaultSchemaInfo->aPropertiesSearchTable,
  1563. g_pDefaultSchemaInfo->nNumOfProperties * 2,
  1564. &(g_pDefaultSchemaInfo->aClasses),
  1565. &(g_pDefaultSchemaInfo->nNumOfClasses),
  1566. &(g_pDefaultSchemaInfo->aClassesSearchTable));
  1567. if ( SUCCEEDED(hr))
  1568. {
  1569. hr = ProcessClassInfoArray( g_pDefaultSchemaInfo->aClasses,
  1570. g_pDefaultSchemaInfo->nNumOfClasses,
  1571. g_pDefaultSchemaInfo->aClassesSearchTable );
  1572. }
  1573. }
  1574. if (FAILED(hr))
  1575. {
  1576. delete g_pDefaultSchemaInfo;
  1577. g_pDefaultSchemaInfo = NULL;
  1578. LEAVE_DEFAULTSCHEMA_CRITSECT();
  1579. }
  1580. BAIL_IF_ERROR(hr);
  1581. }
  1582. LEAVE_DEFAULTSCHEMA_CRITSECT();
  1583. //
  1584. // Allocate an entry for the schema info
  1585. //
  1586. pSchemaInfo = new SCHEMAINFO;
  1587. if ( pSchemaInfo == NULL )
  1588. {
  1589. hr = E_OUTOFMEMORY;
  1590. BAIL_IF_ERROR(hr);
  1591. }
  1592. //
  1593. // Store the server name
  1594. //
  1595. if (pszServer) {
  1596. pSchemaInfo->pszServerName = AllocADsStr( pszServer );
  1597. if ( pSchemaInfo->pszServerName == NULL )
  1598. {
  1599. hr = E_OUTOFMEMORY;
  1600. BAIL_IF_ERROR(hr);
  1601. }
  1602. }
  1603. //
  1604. // Store the name of the user under whose credentials
  1605. // we're reading the schema
  1606. //
  1607. hr = Credentials.GetUserName(&(pSchemaInfo->pszUserName));
  1608. BAIL_IF_ERROR(hr);
  1609. pSchemaInfo->aClasses = g_pDefaultSchemaInfo->aClasses;
  1610. pSchemaInfo->nNumOfClasses = g_pDefaultSchemaInfo->nNumOfClasses;
  1611. pSchemaInfo->aClassesSearchTable = g_pDefaultSchemaInfo->aClassesSearchTable;
  1612. pSchemaInfo->aProperties = g_pDefaultSchemaInfo->aProperties;
  1613. pSchemaInfo->nNumOfProperties = g_pDefaultSchemaInfo->nNumOfProperties;
  1614. pSchemaInfo->aPropertiesSearchTable = g_pDefaultSchemaInfo->aPropertiesSearchTable;
  1615. pSchemaInfo->fDefaultSchema = TRUE;
  1616. *ppSchemaInfo = pSchemaInfo;
  1617. cleanup:
  1618. if ( FAILED(hr) && pSchemaInfo )
  1619. delete pSchemaInfo;
  1620. RRETURN(hr);
  1621. }
  1622. HRESULT FillPropertyInfoArray(
  1623. LPTSTR *aAttrTypes,
  1624. DWORD dwCount,
  1625. PROPERTYINFO **paProperties,
  1626. DWORD *pnProperties,
  1627. SEARCHENTRY **paPropertiesSearchTable
  1628. )
  1629. {
  1630. HRESULT hr = S_OK;
  1631. DWORD i = 0;
  1632. PROPERTYINFO * pPropArray = NULL;
  1633. PROPERTYINFO * pNewPropArray = NULL;
  1634. LPWSTR *ppszNewNames = NULL;
  1635. DWORD dwNewNameCount = 0;
  1636. DWORD dwDisplacement = 0;
  1637. BOOL fFreeNames = TRUE;
  1638. *paProperties = NULL;
  1639. *pnProperties = 0;
  1640. *paPropertiesSearchTable = NULL;
  1641. if ( dwCount == 0 )
  1642. RRETURN(S_OK);
  1643. pPropArray = (PROPERTYINFO *)AllocADsMem( sizeof(PROPERTYINFO) * dwCount);
  1644. if (!pPropArray) {
  1645. hr = E_OUTOFMEMORY;
  1646. BAIL_ON_FAILURE(hr);
  1647. }
  1648. for ( i = 0; i < dwCount; i++) {
  1649. fFreeNames = FREE_ALL_BUT_FIRST;
  1650. dwNewNameCount = 0;
  1651. pPropArray[i].dwUsage = ATTR_USAGE_USERAPPLICATIONS;
  1652. hr = AttributeTypeDescription(
  1653. aAttrTypes[i],
  1654. pPropArray + (i+dwDisplacement),
  1655. &ppszNewNames,
  1656. &dwNewNameCount
  1657. );
  1658. BAIL_ON_FAILURE(hr);
  1659. if (ppszNewNames) {
  1660. if (dwNewNameCount > 1) {
  1661. hr = AddNewNamesToPropertyArray(
  1662. &pPropArray,
  1663. i + dwDisplacement, // current position in array
  1664. dwCount + dwDisplacement, // total number already in array
  1665. ppszNewNames, // array of names.
  1666. dwNewNameCount // number of names
  1667. );
  1668. //
  1669. // In the failure case, we can still continue, just
  1670. // that the additional names will not be recognized.
  1671. //
  1672. if (SUCCEEDED(hr)) {
  1673. //
  1674. // In this case the new array has the data needed.
  1675. // The count is updated suitably only on success.
  1676. //
  1677. fFreeNames = FALSE;
  1678. dwDisplacement += (dwNewNameCount - 1);
  1679. }
  1680. }
  1681. FreeDirectoryStrings(
  1682. ppszNewNames,
  1683. dwNewNameCount,
  1684. fFreeNames ?
  1685. FREE_ALL_BUT_FIRST : FREE_ARRAY_NOT_ELEMENTS
  1686. );
  1687. ppszNewNames = NULL; // freed in the above call.
  1688. }
  1689. }
  1690. //
  1691. // Reduce i by one in case we fail and call FreePropertyInfoArray below.
  1692. //
  1693. i--;
  1694. dwCount += dwDisplacement;
  1695. hr = ProcessPropertyInfoArray(pPropArray, dwCount, paPropertiesSearchTable);
  1696. BAIL_ON_FAILURE(hr);
  1697. *paProperties = pPropArray;
  1698. *pnProperties = dwCount;
  1699. RRETURN(S_OK);
  1700. error:
  1701. FreePropertyInfoArray( pPropArray, i + 1);
  1702. //
  1703. // Need to free the new names list if it is valid.
  1704. //
  1705. if (ppszNewNames) {
  1706. //
  1707. // This function frees pszNames too.
  1708. //
  1709. FreeDirectoryStrings(
  1710. ppszNewNames,
  1711. dwNewNameCount,
  1712. FREE_ALL_BUT_FIRST // do not free first element
  1713. );
  1714. }
  1715. RRETURN(hr);
  1716. }
  1717. HRESULT
  1718. GetInfoFromSuperiorProperty(
  1719. PROPERTYINFO *pPropertyInfo,
  1720. PROPERTYINFO *pPropertyInfoSuperior
  1721. )
  1722. {
  1723. HRESULT hr = S_OK;
  1724. if ( pPropertyInfo->pszSyntax == NULL )
  1725. {
  1726. pPropertyInfo->pszSyntax =
  1727. AllocADsStr( pPropertyInfoSuperior->pszSyntax );
  1728. if ( pPropertyInfo->pszSyntax == NULL )
  1729. {
  1730. hr = E_OUTOFMEMORY;
  1731. BAIL_IF_ERROR(hr);
  1732. }
  1733. }
  1734. #if 0
  1735. if ( pPropertyInfo->lMaxRange == 0
  1736. && pPropertyInfo->lMinRange == 0
  1737. )
  1738. {
  1739. pPropertyInfo->lMaxRange = pPropertyInfoSuperior->lMaxRange;
  1740. pPropertyInfo->lMinRange = pPropertyInfoSuperior->lMinRange;
  1741. }
  1742. #endif
  1743. if ( pPropertyInfoSuperior->fSingleValued
  1744. && !pPropertyInfo->fSingleValued
  1745. )
  1746. {
  1747. pPropertyInfo->fSingleValued = pPropertyInfoSuperior->fSingleValued;
  1748. }
  1749. if ( pPropertyInfo->pszOIDEquality == NULL
  1750. && pPropertyInfoSuperior->pszOIDEquality != NULL
  1751. )
  1752. {
  1753. pPropertyInfo->pszOIDEquality =
  1754. AllocADsStr( pPropertyInfoSuperior->pszOIDEquality );
  1755. if ( pPropertyInfo->pszOIDEquality == NULL )
  1756. {
  1757. hr = E_OUTOFMEMORY;
  1758. BAIL_IF_ERROR(hr);
  1759. }
  1760. }
  1761. if ( pPropertyInfo->pszOIDOrdering == NULL
  1762. && pPropertyInfoSuperior->pszOIDOrdering != NULL
  1763. )
  1764. {
  1765. pPropertyInfo->pszOIDOrdering =
  1766. AllocADsStr( pPropertyInfoSuperior->pszOIDOrdering );
  1767. if ( pPropertyInfo->pszOIDOrdering == NULL )
  1768. {
  1769. hr = E_OUTOFMEMORY;
  1770. BAIL_IF_ERROR(hr);
  1771. }
  1772. }
  1773. if ( pPropertyInfo->pszOIDSubstr == NULL
  1774. && pPropertyInfoSuperior->pszOIDSubstr != NULL
  1775. )
  1776. {
  1777. pPropertyInfo->pszOIDSubstr =
  1778. AllocADsStr( pPropertyInfoSuperior->pszOIDSubstr );
  1779. if ( pPropertyInfo->pszOIDSubstr == NULL )
  1780. {
  1781. hr = E_OUTOFMEMORY;
  1782. BAIL_IF_ERROR(hr);
  1783. }
  1784. }
  1785. cleanup:
  1786. RRETURN(hr);
  1787. }
  1788. HRESULT ProcessPropertyInfoArray(
  1789. PROPERTYINFO *aProperties,
  1790. DWORD nProperties,
  1791. SEARCHENTRY **paPropertiesSearchTable
  1792. )
  1793. {
  1794. HRESULT hr = S_OK;
  1795. SEARCHENTRY *aSearchTable = NULL;
  1796. SEARCHENTRY searchEntry;
  1797. SEARCHENTRY *matchedEntry;
  1798. DWORD i;
  1799. BOOL fProcessedAll = TRUE;
  1800. DWORD nLoop = 0;
  1801. *paPropertiesSearchTable = NULL;
  1802. //
  1803. // First, build a binary search table for faster lookup
  1804. //
  1805. aSearchTable = (SEARCHENTRY *) AllocADsMem(
  1806. sizeof(SEARCHENTRY) * nProperties * 2);
  1807. if (!aSearchTable) {
  1808. hr = E_OUTOFMEMORY;
  1809. BAIL_ON_FAILURE(hr);
  1810. }
  1811. for ( i = 0; i < nProperties; i++ )
  1812. {
  1813. // OIDs can be specified in 2.5.6.0 format or name.
  1814. // So, we need both entries in the search table.
  1815. //
  1816. // Special case to handle no names or OID's
  1817. //
  1818. if (aProperties[i].pszPropertyName == NULL) {
  1819. aProperties[i].pszPropertyName = AllocADsStr(aProperties[i].pszOID);
  1820. }
  1821. if (aProperties[i].pszOID == NULL) {
  1822. aProperties[i].pszOID = AllocADsStr(aProperties[i].pszPropertyName);
  1823. }
  1824. if(!aProperties[i].pszPropertyName || !aProperties[i].pszOID)
  1825. {
  1826. hr = E_OUTOFMEMORY;
  1827. BAIL_ON_FAILURE(hr);
  1828. }
  1829. aSearchTable[2*i].pszName = aProperties[i].pszPropertyName;
  1830. aSearchTable[2*i].nIndex = i;
  1831. aSearchTable[2*i+1].pszName = aProperties[i].pszOID;
  1832. aSearchTable[2*i+1].nIndex = i;
  1833. }
  1834. //
  1835. // Sort the table
  1836. //
  1837. qsort( aSearchTable, 2*nProperties, sizeof(SEARCHENTRY), searchentrycmp );
  1838. do {
  1839. fProcessedAll = TRUE;
  1840. for ( DWORD i = 0; i < nProperties; i++ )
  1841. {
  1842. LPTSTR pszOIDSup = NULL;
  1843. if ( aProperties[i].fProcessedSuperiorClass )
  1844. continue;
  1845. pszOIDSup = aProperties[i].pszOIDSup;
  1846. if ( pszOIDSup == NULL )
  1847. {
  1848. aProperties[i].fProcessedSuperiorClass = TRUE;
  1849. continue;
  1850. }
  1851. searchEntry.pszName = pszOIDSup;
  1852. matchedEntry = (SEARCHENTRY *) bsearch(
  1853. (SEARCHENTRY *) &searchEntry,
  1854. aSearchTable, 2*nProperties,
  1855. sizeof(SEARCHENTRY), searchentrycmp );
  1856. if ( matchedEntry ) // found the superior class
  1857. {
  1858. if (!aProperties[matchedEntry->nIndex].fProcessedSuperiorClass)
  1859. {
  1860. fProcessedAll = FALSE;
  1861. continue;
  1862. }
  1863. hr = GetInfoFromSuperiorProperty(
  1864. &(aProperties[i]), &(aProperties[matchedEntry->nIndex]));
  1865. BAIL_ON_FAILURE(hr);
  1866. }
  1867. aProperties[i].fProcessedSuperiorClass = TRUE;
  1868. }
  1869. nLoop++;
  1870. } while ( (nLoop < MAX_LOOP_COUNT) && !fProcessedAll );
  1871. *paPropertiesSearchTable = aSearchTable;
  1872. RRETURN(S_OK);
  1873. error:
  1874. if ( aSearchTable )
  1875. FreeADsMem( aSearchTable );
  1876. RRETURN(hr);
  1877. }
  1878. VOID FreePropertyInfoArray(
  1879. PROPERTYINFO *aProperties,
  1880. DWORD nProperties
  1881. )
  1882. {
  1883. if ( aProperties )
  1884. {
  1885. DWORD j;
  1886. for ( j = 0; j < nProperties; j++ )
  1887. {
  1888. FreeADsStr( aProperties[j].pszPropertyName );
  1889. FreeADsStr( aProperties[j].pszOID );
  1890. FreeADsStr( aProperties[j].pszSyntax );
  1891. FreeADsStr( aProperties[j].pszDescription );
  1892. FreeADsStr( aProperties[j].pszOIDSup );
  1893. FreeADsStr( aProperties[j].pszOIDEquality );
  1894. FreeADsStr( aProperties[j].pszOIDOrdering );
  1895. FreeADsStr( aProperties[j].pszOIDSubstr );
  1896. }
  1897. FreeADsMem( aProperties );
  1898. }
  1899. }
  1900. HRESULT FillClassInfoArray(
  1901. LPTSTR *aObjectClasses,
  1902. DWORD dwCount,
  1903. SEARCHENTRY *aPropSearchTable,
  1904. DWORD dwSearchTableCount,
  1905. CLASSINFO **paClasses,
  1906. DWORD *pnClasses,
  1907. SEARCHENTRY **paClassesSearchTable
  1908. )
  1909. {
  1910. HRESULT hr = S_OK;
  1911. DWORD i = 0;
  1912. CLASSINFO * pClassArray = NULL;
  1913. SEARCHENTRY *aSearchTable = NULL;
  1914. LPWSTR *ppszNewNames = NULL;
  1915. DWORD dwNewNameCount = 0;
  1916. DWORD dwDisplacement = 0;
  1917. BOOL fFreeNames = FALSE;
  1918. *paClasses= NULL;
  1919. *pnClasses= 0;
  1920. *paClassesSearchTable = NULL;
  1921. if ( dwCount == 0 )
  1922. RRETURN(S_OK);
  1923. pClassArray = (CLASSINFO *)AllocADsMem( sizeof(CLASSINFO) * dwCount );
  1924. if (!pClassArray) {
  1925. hr = E_OUTOFMEMORY;
  1926. BAIL_ON_FAILURE(hr);
  1927. }
  1928. for ( i = 0; i < dwCount; i++) {
  1929. ppszNewNames = NULL;
  1930. dwNewNameCount = 0;
  1931. fFreeNames = TRUE;
  1932. pClassArray[i].IsContainer = -1;
  1933. pClassArray[i].dwType = CLASS_TYPE_STRUCTURAL;
  1934. hr = ObjectClassDescription(
  1935. aObjectClasses[i],
  1936. pClassArray + (i + dwDisplacement),
  1937. aPropSearchTable,
  1938. dwSearchTableCount,
  1939. &ppszNewNames,
  1940. &dwNewNameCount
  1941. );
  1942. BAIL_ON_FAILURE(hr);
  1943. if (ppszNewNames) {
  1944. if (dwNewNameCount > 1) {
  1945. //
  1946. // ********************** IMPORTANT NOTE ************
  1947. // In this routine, we specifically do not duplicate
  1948. // the pCLSID and pPrimaryInterfaceGUID as these are not
  1949. // freed when we exit and do not appear to be used anywehre.
  1950. // If ObjectClasDescription is changed to add this info,
  1951. // then the fn below needs to be update accordingly.
  1952. // **************************************************
  1953. //
  1954. hr = AddNewNamesToClassArray(
  1955. &pClassArray,
  1956. i + dwDisplacement, // current position in array
  1957. dwCount + dwDisplacement, // total number already in array
  1958. ppszNewNames, // array of names.
  1959. dwNewNameCount // number of names
  1960. );
  1961. //
  1962. // In the failure case, we can still continue, just
  1963. // that the additional names will not be recognized.
  1964. //
  1965. if (SUCCEEDED(hr)) {
  1966. //
  1967. // In this case the new array has the data needed.
  1968. // The count is updated suitably only on success.
  1969. //
  1970. fFreeNames = FALSE;
  1971. dwDisplacement += (dwNewNameCount - 1);
  1972. }
  1973. }
  1974. FreeDirectoryStrings(
  1975. ppszNewNames,
  1976. dwNewNameCount,
  1977. fFreeNames ?
  1978. FREE_ALL_BUT_FIRST : FREE_ARRAY_NOT_ELEMENTS
  1979. );
  1980. ppszNewNames = NULL; // freed in the above call.
  1981. } // if newNames is valid.
  1982. } // for
  1983. //
  1984. // Count should now include the new elements added.
  1985. //
  1986. dwCount += dwDisplacement;
  1987. //
  1988. // Build a binary search table for faster lookup
  1989. //
  1990. aSearchTable = (SEARCHENTRY *) AllocADsMem(
  1991. sizeof(SEARCHENTRY) * dwCount * 2);
  1992. if (!aSearchTable) {
  1993. hr = E_OUTOFMEMORY;
  1994. //
  1995. // i is now dwCount but should be one less as
  1996. // the free call is made with i+1
  1997. //
  1998. i--;
  1999. BAIL_ON_FAILURE(hr);
  2000. }
  2001. for ( i = 0; i < dwCount; i++ )
  2002. {
  2003. //
  2004. // Take care of the NULL name/OID in case of some servers
  2005. //
  2006. if (pClassArray[i].pszName == NULL) {
  2007. pClassArray[i].pszName = AllocADsStr(pClassArray[i].pszOID);
  2008. }
  2009. if (pClassArray[i].pszOID == NULL) {
  2010. pClassArray[i].pszOID = AllocADsStr(pClassArray[i].pszName);
  2011. }
  2012. if(!pClassArray[i].pszName || !pClassArray[i].pszOID)
  2013. {
  2014. hr = E_OUTOFMEMORY;
  2015. BAIL_ON_FAILURE(hr);
  2016. }
  2017. // OIDs can be specified in 2.5.6.0 format or name.
  2018. // So, we need both entries in the search table.
  2019. aSearchTable[2*i].pszName = pClassArray[i].pszName;
  2020. aSearchTable[2*i].nIndex = i;
  2021. aSearchTable[2*i+1].pszName = pClassArray[i].pszOID;
  2022. aSearchTable[2*i+1].nIndex = i;
  2023. }
  2024. //
  2025. // Sort the table
  2026. //
  2027. qsort( aSearchTable, 2*dwCount, sizeof(SEARCHENTRY), searchentrycmp );
  2028. *paClasses = pClassArray;
  2029. *pnClasses = dwCount;
  2030. *paClassesSearchTable = aSearchTable;
  2031. RRETURN(S_OK);
  2032. error:
  2033. FreeClassInfoArray( pClassArray, i + 1 );
  2034. if ( aSearchTable )
  2035. FreeADsMem( aSearchTable );
  2036. if (ppszNewNames) {
  2037. FreeDirectoryStrings(
  2038. ppszNewNames,
  2039. dwNewNameCount,
  2040. FREE_ALL_BUT_FIRST // first taken care of above.
  2041. );
  2042. }
  2043. RRETURN(hr);
  2044. }
  2045. HRESULT FillAuxClassInfoArray(
  2046. LPTSTR *aDITContentRules,
  2047. DWORD dwCount,
  2048. SEARCHENTRY *aPropSearchTable,
  2049. DWORD dwSearchTableCount,
  2050. CLASSINFO *aClasses,
  2051. DWORD nClasses,
  2052. SEARCHENTRY *aClassesSearchTable
  2053. )
  2054. {
  2055. HRESULT hr = S_OK;
  2056. DWORD i = 0;
  2057. CLASSINFO ClassInfo;
  2058. int index;
  2059. int* pTemp = NULL;
  2060. if ( dwCount == 0 )
  2061. RRETURN(S_OK);
  2062. for ( i = 0; i < dwCount; i++) {
  2063. memset( &ClassInfo, 0, sizeof(ClassInfo));
  2064. hr = DITContentRuleDescription( aDITContentRules[i], &ClassInfo,
  2065. aPropSearchTable, dwSearchTableCount );
  2066. BAIL_ON_FAILURE(hr);
  2067. index = FindEntryInSearchTable( ClassInfo.pszOID,
  2068. aClassesSearchTable,
  2069. nClasses * 2 );
  2070. if ( index == -1 )
  2071. continue; // Cannot find the class, ignore and continue
  2072. aClasses[index].pOIDsNotContain = ClassInfo.pOIDsNotContain;
  2073. aClasses[index].nNumOfNotContain = ClassInfo.nNumOfNotContain;
  2074. aClasses[index].pOIDsAuxClasses = ClassInfo.pOIDsAuxClasses;
  2075. if ( ClassInfo.pOIDsMustContain )
  2076. {
  2077. DWORD nMustContain = aClasses[index].nNumOfMustContain;
  2078. if ( nMustContain == 0 )
  2079. {
  2080. aClasses[index].pOIDsMustContain = ClassInfo.pOIDsMustContain;
  2081. aClasses[index].nNumOfMustContain = ClassInfo.nNumOfMustContain;
  2082. }
  2083. else
  2084. {
  2085. pTemp =
  2086. (int *) ReallocADsMem( aClasses[index].pOIDsMustContain,
  2087. sizeof(int) * nMustContain,
  2088. sizeof(int) * ( nMustContain +
  2089. ClassInfo.nNumOfMustContain + 1));
  2090. if ( pTemp == NULL )
  2091. {
  2092. hr = E_OUTOFMEMORY;
  2093. BAIL_ON_FAILURE(hr);
  2094. }
  2095. aClasses[index].pOIDsMustContain = pTemp;
  2096. pTemp = NULL;
  2097. memcpy( &(aClasses[index].pOIDsMustContain[nMustContain]),
  2098. ClassInfo.pOIDsMustContain,
  2099. ClassInfo.nNumOfMustContain * sizeof(int));
  2100. aClasses[index].nNumOfMustContain += ClassInfo.nNumOfMustContain;
  2101. //
  2102. // Need to terminate with -1.
  2103. //
  2104. aClasses[index].pOIDsMustContain[nMustContain+ClassInfo.nNumOfMustContain] = -1;
  2105. FreeADsMem( ClassInfo.pOIDsMustContain );
  2106. ClassInfo.pOIDsMustContain = NULL;
  2107. }
  2108. }
  2109. if ( ClassInfo.pOIDsMayContain )
  2110. {
  2111. DWORD nMayContain = aClasses[index].nNumOfMayContain;
  2112. if ( nMayContain == 0 )
  2113. {
  2114. aClasses[index].pOIDsMayContain = ClassInfo.pOIDsMayContain;
  2115. aClasses[index].nNumOfMayContain = ClassInfo.nNumOfMayContain;
  2116. }
  2117. else
  2118. {
  2119. pTemp =
  2120. (int *) ReallocADsMem( aClasses[index].pOIDsMayContain,
  2121. sizeof(int) * nMayContain,
  2122. sizeof(int) * ( nMayContain +
  2123. ClassInfo.nNumOfMayContain + 1));
  2124. if ( pTemp == NULL )
  2125. {
  2126. hr = E_OUTOFMEMORY;
  2127. BAIL_ON_FAILURE(hr);
  2128. }
  2129. aClasses[index].pOIDsMayContain = pTemp;
  2130. pTemp = NULL;
  2131. memcpy( &(aClasses[index].pOIDsMayContain[nMayContain]),
  2132. ClassInfo.pOIDsMayContain,
  2133. ClassInfo.nNumOfMayContain * sizeof(int));
  2134. aClasses[index].nNumOfMayContain += ClassInfo.nNumOfMayContain;
  2135. //
  2136. // Need to terminate with -1.
  2137. //
  2138. aClasses[index].pOIDsMayContain[nMayContain+ClassInfo.nNumOfMayContain] = -1;
  2139. FreeADsMem( ClassInfo.pOIDsMayContain );
  2140. ClassInfo.pOIDsMayContain = NULL;
  2141. }
  2142. }
  2143. FreeADsStr( ClassInfo.pszOID );
  2144. ClassInfo.pszOID = NULL;
  2145. FreeADsStr( ClassInfo.pszName );
  2146. FreeADsStr( ClassInfo.pszDescription );
  2147. }
  2148. RRETURN(S_OK);
  2149. error:
  2150. if ( ClassInfo.pszOID )
  2151. {
  2152. FreeADsStr( ClassInfo.pszOID );
  2153. FreeADsStr( ClassInfo.pszName );
  2154. FreeADsStr( ClassInfo.pszDescription );
  2155. if ( ClassInfo.pOIDsMustContain )
  2156. {
  2157. FreeADsMem( ClassInfo.pOIDsMustContain );
  2158. }
  2159. if ( ClassInfo.pOIDsMayContain )
  2160. {
  2161. FreeADsMem( ClassInfo.pOIDsMayContain );
  2162. }
  2163. }
  2164. RRETURN(hr);
  2165. }
  2166. HRESULT
  2167. GetInfoFromSuperiorClasses(
  2168. CLASSINFO *pClassInfo,
  2169. CLASSINFO *pClassInfoSuperior
  2170. )
  2171. {
  2172. HRESULT hr = S_OK;
  2173. DWORD i;
  2174. int *pOIDsMustSup = pClassInfoSuperior->pOIDsMustContain;
  2175. int *pOIDsMaySup = pClassInfoSuperior->pOIDsMayContain;
  2176. DWORD nCountMustSup = pClassInfoSuperior->nNumOfMustContain;
  2177. DWORD nCountMaySup = pClassInfoSuperior->nNumOfMayContain;
  2178. int *pOIDsMust = pClassInfo->pOIDsMustContain;
  2179. int *pOIDsMay = pClassInfo->pOIDsMayContain;
  2180. DWORD nCountMust = pClassInfo->nNumOfMustContain;
  2181. DWORD nCountMay = pClassInfo->nNumOfMayContain;
  2182. int *pOIDsMustNew = NULL;
  2183. int *pOIDsMayNew = NULL;
  2184. if ( pOIDsMaySup == NULL && pOIDsMustSup == NULL )
  2185. RRETURN(S_OK);
  2186. if ( nCountMustSup > 0 )
  2187. {
  2188. pOIDsMustNew = (int *) AllocADsMem(
  2189. sizeof(int) * (nCountMust + nCountMustSup + 1));
  2190. if ( pOIDsMustNew == NULL )
  2191. {
  2192. hr = E_OUTOFMEMORY;
  2193. BAIL_IF_ERROR(hr);
  2194. }
  2195. for ( i = 0; i < nCountMustSup; i++ )
  2196. {
  2197. pOIDsMustNew[i] = pOIDsMustSup[i];
  2198. }
  2199. for ( i = 0; i < nCountMust; i++ )
  2200. {
  2201. pOIDsMustNew[i+nCountMustSup] = pOIDsMust[i];
  2202. }
  2203. pOIDsMustNew[nCountMustSup+nCountMust] = -1;
  2204. pClassInfo->pOIDsMustContain = pOIDsMustNew;
  2205. pClassInfo->nNumOfMustContain = nCountMust + nCountMustSup;
  2206. if ( pOIDsMust )
  2207. FreeADsMem( pOIDsMust );
  2208. pOIDsMustNew = NULL;
  2209. }
  2210. if ( nCountMaySup > 0 )
  2211. {
  2212. pOIDsMayNew = (int *) AllocADsMem(
  2213. sizeof(int) * ( nCountMay + nCountMaySup + 1 ));
  2214. if ( pOIDsMayNew == NULL )
  2215. {
  2216. hr = E_OUTOFMEMORY;
  2217. BAIL_IF_ERROR(hr);
  2218. }
  2219. for ( i = 0; i < nCountMaySup; i++ )
  2220. {
  2221. pOIDsMayNew[i] = pOIDsMaySup[i];
  2222. }
  2223. for ( i = 0; i < nCountMay; i++ )
  2224. {
  2225. pOIDsMayNew[i+nCountMaySup] = pOIDsMay[i];
  2226. }
  2227. pOIDsMayNew[nCountMaySup+nCountMay] = -1;
  2228. pClassInfo->pOIDsMayContain = pOIDsMayNew;
  2229. pClassInfo->nNumOfMayContain = nCountMay + nCountMaySup;
  2230. if ( pOIDsMay )
  2231. FreeADsMem( pOIDsMay );
  2232. pOIDsMayNew = NULL;
  2233. }
  2234. cleanup:
  2235. if (FAILED(hr))
  2236. {
  2237. if ( pOIDsMustNew )
  2238. FreeADsMem( pOIDsMustNew );
  2239. if ( pOIDsMayNew )
  2240. FreeADsMem( pOIDsMayNew );
  2241. }
  2242. RRETURN(hr);
  2243. }
  2244. HRESULT ProcessClassInfoArray(
  2245. CLASSINFO *aClasses,
  2246. DWORD nClasses,
  2247. SEARCHENTRY *aSearchTable,
  2248. BOOL fProcessAUX // defaulted to false
  2249. )
  2250. {
  2251. HRESULT hr = S_OK;
  2252. SEARCHENTRY searchEntry;
  2253. SEARCHENTRY *matchedEntry;
  2254. DWORD i;
  2255. BOOL fProcessedAll = TRUE;
  2256. DWORD nLoop = 0;
  2257. do
  2258. {
  2259. fProcessedAll = TRUE;
  2260. for ( DWORD i = 0; i < nClasses; i++ )
  2261. {
  2262. LPTSTR *pOIDsSup = NULL;
  2263. LPTSTR *pOIDsAux = NULL;
  2264. DWORD j = 0;
  2265. if ( aClasses[i].fProcessedSuperiorClasses )
  2266. continue;
  2267. pOIDsSup = aClasses[i].pOIDsSuperiorClasses;
  2268. //
  2269. // If fProcessAUX then we ne need to add the aux
  2270. // class lists must and may to the classes own list.
  2271. // Please look at where this flag is being set for
  2272. // more details.
  2273. //
  2274. if (fProcessAUX) {
  2275. pOIDsAux = aClasses[i].pOIDsAuxClasses;
  2276. }
  2277. else {
  2278. pOIDsAux = NULL;
  2279. }
  2280. if ( pOIDsSup == NULL )
  2281. {
  2282. aClasses[i].fProcessedSuperiorClasses = TRUE;
  2283. continue;
  2284. }
  2285. // This is here just in case the schema description for class "top"
  2286. // has other superior classes. "top" should not have any superior
  2287. // classes.
  2288. if ( _tcsicmp( aClasses[i].pszName, TEXT("top")) == 0 )
  2289. {
  2290. aClasses[i].fProcessedSuperiorClasses = TRUE;
  2291. continue;
  2292. }
  2293. j = 0;
  2294. while ( pOIDsSup[j] )
  2295. {
  2296. searchEntry.pszName = pOIDsSup[j];
  2297. matchedEntry = (SEARCHENTRY *) bsearch(
  2298. (SEARCHENTRY *) &searchEntry,
  2299. aSearchTable, 2*nClasses,
  2300. sizeof(SEARCHENTRY), searchentrycmp );
  2301. if ( matchedEntry ) // found the superior class
  2302. {
  2303. if (!aClasses[matchedEntry->nIndex].fProcessedSuperiorClasses)
  2304. {
  2305. fProcessedAll = FALSE;
  2306. break;
  2307. }
  2308. hr = GetInfoFromSuperiorClasses(
  2309. &(aClasses[i]), &(aClasses[matchedEntry->nIndex]));
  2310. BAIL_ON_FAILURE(hr);
  2311. }
  2312. j++;
  2313. }
  2314. if ( pOIDsSup[j] == NULL )
  2315. {
  2316. if ( pOIDsAux == NULL )
  2317. {
  2318. aClasses[i].fProcessedSuperiorClasses = TRUE;
  2319. }
  2320. else
  2321. {
  2322. j = 0;
  2323. while ( pOIDsAux[j] )
  2324. {
  2325. searchEntry.pszName = pOIDsAux[j];
  2326. matchedEntry = (SEARCHENTRY *) bsearch(
  2327. (SEARCHENTRY *) &searchEntry,
  2328. aSearchTable, 2*nClasses,
  2329. sizeof(SEARCHENTRY), searchentrycmp);
  2330. if ( matchedEntry ) // found the superior class
  2331. {
  2332. if (!aClasses[matchedEntry->nIndex].fProcessedSuperiorClasses)
  2333. {
  2334. fProcessedAll = FALSE;
  2335. break;
  2336. }
  2337. hr = GetInfoFromSuperiorClasses(
  2338. &(aClasses[i]),
  2339. &(aClasses[matchedEntry->nIndex]));
  2340. BAIL_ON_FAILURE(hr);
  2341. }
  2342. j++;
  2343. }
  2344. if ( pOIDsAux[j] == NULL )
  2345. aClasses[i].fProcessedSuperiorClasses = TRUE;
  2346. }
  2347. }
  2348. }
  2349. nLoop++;
  2350. } while ( (nLoop < MAX_LOOP_COUNT) && !fProcessedAll );
  2351. for ( i = 0; i < nClasses; i++ )
  2352. {
  2353. CLASSINFO *pClass = &(aClasses[i]);
  2354. DWORD j, k;
  2355. //
  2356. // Eliminate duplicates in MustContain
  2357. //
  2358. if ( pClass->pOIDsMustContain != NULL )
  2359. {
  2360. SortAndRemoveDuplicateOIDs( pClass->pOIDsMustContain,
  2361. &pClass->nNumOfMustContain );
  2362. }
  2363. //
  2364. // Eliminate duplicates in MayContain
  2365. //
  2366. if ( pClass->pOIDsMayContain != NULL )
  2367. {
  2368. SortAndRemoveDuplicateOIDs( pClass->pOIDsMayContain,
  2369. &pClass->nNumOfMayContain );
  2370. }
  2371. //
  2372. // Eliminate duplicates between MustContain and MayContain
  2373. //
  2374. if ( ( pClass->pOIDsMustContain == NULL )
  2375. || ( pClass->pOIDsMayContain == NULL )
  2376. )
  2377. {
  2378. continue;
  2379. }
  2380. j = 0; k = 0;
  2381. while ( ( pClass->pOIDsMustContain[j] != -1 )
  2382. && ( pClass->pOIDsMayContain[k] != -1 )
  2383. )
  2384. {
  2385. int nMust = pClass->pOIDsMustContain[j];
  2386. int nMay = pClass->pOIDsMayContain[k];
  2387. if ( nMust < nMay )
  2388. {
  2389. j++;
  2390. }
  2391. else if ( nMust > nMay )
  2392. {
  2393. k++;
  2394. }
  2395. else // nMust == nMay
  2396. {
  2397. j++;
  2398. DWORD m = k;
  2399. do {
  2400. pClass->pOIDsMayContain[m] = pClass->pOIDsMayContain[m+1];
  2401. m++;
  2402. } while ( pClass->pOIDsMayContain[m] != -1 );
  2403. }
  2404. }
  2405. //
  2406. // Removes the NotContain from the MustContain or MayContain list
  2407. //
  2408. if ( pClass->pOIDsNotContain != NULL )
  2409. {
  2410. qsort( pClass->pOIDsNotContain, pClass->nNumOfNotContain,
  2411. sizeof(pClass->pOIDsNotContain[0]), intcmp );
  2412. j = 0; k = 0;
  2413. while ( ( pClass->pOIDsMustContain[j] != -1 )
  2414. && ( pClass->pOIDsNotContain[k] != -1 )
  2415. )
  2416. {
  2417. int nMust = pClass->pOIDsMustContain[j];
  2418. int nNot = pClass->pOIDsNotContain[k];
  2419. if ( nMust < nNot )
  2420. {
  2421. j++;
  2422. }
  2423. else if ( nMust > nNot )
  2424. {
  2425. k++;
  2426. }
  2427. else // nMust == nNot
  2428. {
  2429. k++;
  2430. DWORD m = j;
  2431. do {
  2432. pClass->pOIDsMustContain[m] = pClass->pOIDsMustContain[m+1];
  2433. m++;
  2434. } while ( pClass->pOIDsMustContain[m] != -1 );
  2435. }
  2436. }
  2437. j = 0; k = 0;
  2438. while ( ( pClass->pOIDsMayContain[j] != -1 )
  2439. && ( pClass->pOIDsNotContain[k] != -1 )
  2440. )
  2441. {
  2442. int nMay = pClass->pOIDsMayContain[j];
  2443. int nNot = pClass->pOIDsNotContain[k];
  2444. if ( nMay < nNot )
  2445. {
  2446. j++;
  2447. }
  2448. else if ( nMay > nNot )
  2449. {
  2450. k++;
  2451. }
  2452. else // nMay == nNot
  2453. {
  2454. k++;
  2455. DWORD m = j;
  2456. do {
  2457. pClass->pOIDsMayContain[m] = pClass->pOIDsMayContain[m+1];
  2458. m++;
  2459. } while ( pClass->pOIDsMayContain[m] != -1 );
  2460. }
  2461. }
  2462. FreeADsMem( pClass->pOIDsNotContain );
  2463. pClass->pOIDsNotContain = NULL;
  2464. }
  2465. }
  2466. RRETURN(S_OK);
  2467. error:
  2468. RRETURN(hr);
  2469. }
  2470. VOID SortAndRemoveDuplicateOIDs(
  2471. int *aOIDs,
  2472. DWORD *pnNumOfOIDs
  2473. )
  2474. {
  2475. DWORD j, k;
  2476. qsort( aOIDs, *pnNumOfOIDs, sizeof( aOIDs[0]), intcmp );
  2477. // The following code removes duplicate strings in place.
  2478. // index j is the index to walk the array, and index k is the
  2479. // index of the last non-duplicate entry. The array is sorted
  2480. // and so we compare the string at index j and index k.
  2481. // (1) If they are the same, then j++
  2482. // (2) If not the same, increment k, free the string at k and
  2483. // make k point to the string at j. Then increment j and
  2484. // continue.
  2485. //
  2486. // NOTE: aOIDs must be an array of integers that ends with -1.
  2487. j = 1; k = 0;
  2488. while ( aOIDs[j] != -1 )
  2489. {
  2490. while ( aOIDs[j] == aOIDs[k] )
  2491. {
  2492. j++;
  2493. if ( aOIDs[j] == -1 )
  2494. break;
  2495. }
  2496. if ( aOIDs[j] != -1 )
  2497. {
  2498. k++;
  2499. if ( k != j )
  2500. {
  2501. aOIDs[k] = aOIDs[j];
  2502. aOIDs[j] = -1;
  2503. }
  2504. j++;
  2505. }
  2506. }
  2507. k++;
  2508. aOIDs[k] = -1;
  2509. *pnNumOfOIDs = k;
  2510. }
  2511. VOID FreeClassInfoArray(
  2512. CLASSINFO *aClasses,
  2513. DWORD nClasses
  2514. )
  2515. {
  2516. if ( aClasses )
  2517. {
  2518. DWORD j;
  2519. for ( j = 0; j < nClasses; j++ )
  2520. {
  2521. FreeADsStr( aClasses[j].pszName );
  2522. FreeADsStr( aClasses[j].pszOID );
  2523. FreeADsStr( aClasses[j].pszHelpFileName );
  2524. FreeADsStr( aClasses[j].pszDescription );
  2525. DWORD k = 0;
  2526. if ( aClasses[j].pOIDsSuperiorClasses )
  2527. {
  2528. while ( aClasses[j].pOIDsSuperiorClasses[k] )
  2529. FreeADsStr( aClasses[j].pOIDsSuperiorClasses[k++]);
  2530. FreeADsMem( aClasses[j].pOIDsSuperiorClasses );
  2531. }
  2532. k = 0;
  2533. if ( aClasses[j].pOIDsAuxClasses )
  2534. {
  2535. while ( aClasses[j].pOIDsAuxClasses[k] )
  2536. FreeADsStr( aClasses[j].pOIDsAuxClasses[k++]);
  2537. FreeADsMem( aClasses[j].pOIDsAuxClasses );
  2538. }
  2539. if ( aClasses[j].pOIDsMustContain )
  2540. {
  2541. FreeADsMem( aClasses[j].pOIDsMustContain );
  2542. }
  2543. if ( aClasses[j].pOIDsMayContain )
  2544. {
  2545. FreeADsMem( aClasses[j].pOIDsMayContain );
  2546. }
  2547. if ( aClasses[j].pOIDsNotContain )
  2548. {
  2549. FreeADsMem( aClasses[j].pOIDsNotContain );
  2550. }
  2551. }
  2552. FreeADsMem( aClasses );
  2553. }
  2554. }
  2555. DWORD ReadSchemaInfoFromFileWithHandle(
  2556. HANDLE hFile,
  2557. LPTSTR **paValuesAttrTypes,
  2558. int *pnCountAttrTypes,
  2559. LPTSTR **paValuesObjClasses,
  2560. int *pnCountObjClasses,
  2561. LPTSTR **paValuesRules,
  2562. int *pnCountRules,
  2563. LPBYTE *pFileBuffer
  2564. )
  2565. {
  2566. DWORD err = NO_ERROR;
  2567. DWORD dwFileSize = 0;
  2568. DWORD dwBytesRead = 0;
  2569. LPTSTR pLine = NULL;
  2570. LPTSTR pEndOfLine = NULL;
  2571. DWORD nCount;
  2572. DWORD nType;
  2573. DWORD dwStatus = NO_ERROR;
  2574. //
  2575. // Even though the calling routine has an exception handler,
  2576. // we need one over here also as this we need to close the file
  2577. // over here, we do not have the file handle in the calling routine.
  2578. //
  2579. __try {
  2580. dwFileSize = GetFileSize( hFile, NULL );
  2581. if ( dwFileSize == -1 )
  2582. {
  2583. err = GetLastError();
  2584. goto cleanup;
  2585. }
  2586. else if ( dwFileSize == 0 )
  2587. {
  2588. err = ERROR_FILE_INVALID;
  2589. goto cleanup;
  2590. }
  2591. *pFileBuffer = (LPBYTE) AllocADsMem( dwFileSize );
  2592. if ( *pFileBuffer == NULL )
  2593. {
  2594. err = ERROR_NOT_ENOUGH_MEMORY;
  2595. goto cleanup;
  2596. }
  2597. if ( !ReadFile( hFile,
  2598. *pFileBuffer,
  2599. dwFileSize,
  2600. &dwBytesRead,
  2601. NULL ))
  2602. {
  2603. err = GetLastError();
  2604. goto cleanup;
  2605. }
  2606. for ( pLine = ((LPTSTR) *pFileBuffer) + 1; // go past Unicode BOM marker
  2607. pLine < (LPTSTR) ( *pFileBuffer + dwFileSize );
  2608. pLine = pEndOfLine + 1 )
  2609. {
  2610. if ( pEndOfLine = _tcschr( pLine, TEXT('\n')))
  2611. *pEndOfLine = 0;
  2612. if ( _tcsicmp( pLine, TEXT("[attributeTypes]")) == 0 )
  2613. {
  2614. nType = ID_ATTRTYPES;
  2615. continue;
  2616. }
  2617. else if ( _tcsicmp( pLine, TEXT("[objectClasses]")) == 0 )
  2618. {
  2619. nType = ID_OBJCLASSES;
  2620. continue;
  2621. }
  2622. else if ( _tcsicmp( pLine, TEXT("[DITContentRules]")) == 0 )
  2623. {
  2624. nType = ID_DITCONTENTRULES;
  2625. continue;
  2626. }
  2627. switch ( nType )
  2628. {
  2629. case ID_ATTRTYPES:
  2630. (*pnCountAttrTypes)++;
  2631. break;
  2632. case ID_OBJCLASSES:
  2633. (*pnCountObjClasses)++;
  2634. break;
  2635. case ID_DITCONTENTRULES:
  2636. (*pnCountRules)++;
  2637. break;
  2638. }
  2639. }
  2640. *paValuesAttrTypes = (LPTSTR *) AllocADsMem( *pnCountAttrTypes * sizeof(LPTSTR));
  2641. if ( *paValuesAttrTypes == NULL )
  2642. {
  2643. err = ERROR_NOT_ENOUGH_MEMORY;
  2644. goto cleanup;
  2645. }
  2646. *paValuesObjClasses = (LPTSTR *) AllocADsMem( *pnCountObjClasses * sizeof(LPTSTR));
  2647. if ( *paValuesObjClasses == NULL )
  2648. {
  2649. err = ERROR_NOT_ENOUGH_MEMORY;
  2650. goto cleanup;
  2651. }
  2652. *paValuesRules = (LPTSTR *) AllocADsMem( *pnCountRules * sizeof(LPTSTR));
  2653. if ( *paValuesRules == NULL )
  2654. {
  2655. err = ERROR_NOT_ENOUGH_MEMORY;
  2656. goto cleanup;
  2657. }
  2658. for ( pLine = ((LPTSTR) *pFileBuffer) + 1;// go past Unicode BOM marker
  2659. pLine < (LPTSTR) ( *pFileBuffer + dwFileSize );
  2660. pLine += ( _tcslen(pLine) + 1) )
  2661. {
  2662. if ( _tcsicmp( pLine, TEXT("[attributeTypes]")) == 0 )
  2663. {
  2664. nCount = 0;
  2665. nType = ID_ATTRTYPES;
  2666. continue;
  2667. }
  2668. else if ( _tcsicmp( pLine, TEXT("[objectClasses]")) == 0 )
  2669. {
  2670. nCount = 0;
  2671. nType = ID_OBJCLASSES;
  2672. continue;
  2673. }
  2674. else if ( _tcsicmp( pLine, TEXT("[DITContentRules]")) == 0 )
  2675. {
  2676. nCount = 0;
  2677. nType = ID_DITCONTENTRULES;
  2678. continue;
  2679. }
  2680. switch ( nType )
  2681. {
  2682. case ID_ATTRTYPES:
  2683. (*paValuesAttrTypes)[nCount++] = pLine;
  2684. break;
  2685. case ID_OBJCLASSES:
  2686. (*paValuesObjClasses)[nCount++] = pLine;
  2687. break;
  2688. case ID_DITCONTENTRULES:
  2689. (*paValuesRules)[nCount++] = pLine;
  2690. break;
  2691. }
  2692. }
  2693. } __except (EXCEPTION_EXECUTE_HANDLER) {
  2694. err = GetExceptionCode();
  2695. if (dwStatus != EXCEPTION_ACCESS_VIOLATION) {
  2696. ADsDebugOut((DEB_ERROR, "Processing Schema Info:Unknown Exception %d\n", err));
  2697. }
  2698. }
  2699. cleanup:
  2700. CloseHandle( hFile );
  2701. if ( err )
  2702. {
  2703. *pnCountAttrTypes = 0;
  2704. *pnCountObjClasses = 0;
  2705. *pnCountRules = 0;
  2706. if ( *paValuesAttrTypes )
  2707. FreeADsMem( *paValuesAttrTypes );
  2708. if ( *paValuesObjClasses )
  2709. FreeADsMem( *paValuesObjClasses );
  2710. if ( *paValuesRules )
  2711. FreeADsMem( *paValuesRules );
  2712. if ( *pFileBuffer )
  2713. FreeADsMem( *pFileBuffer );
  2714. *paValuesAttrTypes = NULL;
  2715. *paValuesObjClasses = NULL;
  2716. *paValuesRules = NULL;
  2717. *pFileBuffer = NULL;
  2718. }
  2719. return err;
  2720. }
  2721. #ifdef WIN95
  2722. DWORD ReadSchemaInfoFromFileA(
  2723. LPSTR pszFile,
  2724. LPTSTR **paValuesAttrTypes,
  2725. int *pnCountAttrTypes,
  2726. LPTSTR **paValuesObjClasses,
  2727. int *pnCountObjClasses,
  2728. LPTSTR **paValuesRules,
  2729. int *pnCountRules,
  2730. LPBYTE *pFileBuffer
  2731. )
  2732. {
  2733. DWORD err = NO_ERROR;
  2734. HANDLE hFile = NULL;
  2735. *pnCountAttrTypes = 0;
  2736. *pnCountObjClasses = 0;
  2737. *pnCountRules = 0;
  2738. hFile = CreateFileA( pszFile,
  2739. GENERIC_READ,
  2740. FILE_SHARE_READ,
  2741. NULL,
  2742. OPEN_EXISTING,
  2743. FILE_ATTRIBUTE_NORMAL,
  2744. NULL );
  2745. if ( hFile == INVALID_HANDLE_VALUE )
  2746. return GetLastError();
  2747. return (
  2748. ReadSchemaInfoFromFileWithHandle(
  2749. hFile,
  2750. paValuesAttrTypes,
  2751. pnCountAttrTypes,
  2752. paValuesObjClasses,
  2753. pnCountObjClasses,
  2754. paValuesRules,
  2755. pnCountRules,
  2756. pFileBuffer
  2757. )
  2758. );
  2759. }
  2760. #endif
  2761. DWORD ReadSchemaInfoFromFileW(
  2762. LPTSTR pszFile,
  2763. LPTSTR **paValuesAttrTypes,
  2764. int *pnCountAttrTypes,
  2765. LPTSTR **paValuesObjClasses,
  2766. int *pnCountObjClasses,
  2767. LPTSTR **paValuesRules,
  2768. int *pnCountRules,
  2769. LPBYTE *pFileBuffer
  2770. )
  2771. {
  2772. DWORD err = NO_ERROR;
  2773. HANDLE hFile = NULL;
  2774. *pnCountAttrTypes = 0;
  2775. *pnCountObjClasses = 0;
  2776. *pnCountRules = 0;
  2777. hFile = CreateFile( pszFile,
  2778. GENERIC_READ,
  2779. FILE_SHARE_READ,
  2780. NULL,
  2781. OPEN_EXISTING,
  2782. FILE_ATTRIBUTE_NORMAL,
  2783. NULL );
  2784. if ( hFile == INVALID_HANDLE_VALUE )
  2785. return GetLastError();
  2786. return (
  2787. ReadSchemaInfoFromFileWithHandle(
  2788. hFile,
  2789. paValuesAttrTypes,
  2790. pnCountAttrTypes,
  2791. paValuesObjClasses,
  2792. pnCountObjClasses,
  2793. paValuesRules,
  2794. pnCountRules,
  2795. pFileBuffer
  2796. )
  2797. );
  2798. }
  2799. DWORD StoreSchemaInfoToFileWithHandle(
  2800. HANDLE hFile,
  2801. LPTSTR *aValuesAttribTypes,
  2802. int nCountAttribTypes,
  2803. LPTSTR *aValuesObjClasses,
  2804. int nCountObjClasses,
  2805. LPTSTR *aValuesRules,
  2806. int nCountRules
  2807. )
  2808. {
  2809. DWORD err = NO_ERROR;
  2810. TCHAR szEndOfLine[2] = TEXT("\n");
  2811. TCHAR szSection[MAX_PATH];
  2812. DWORD dwBytesWritten;
  2813. int i;
  2814. szSection[0] = 0xFEFF; // Unicode BOM marker
  2815. if ( !WriteFile( hFile,
  2816. szSection,
  2817. sizeof(TCHAR),
  2818. &dwBytesWritten,
  2819. NULL ))
  2820. {
  2821. err = GetLastError();
  2822. goto cleanup;
  2823. }
  2824. _tcscpy( szSection, TEXT("[attributeTypes]\n"));
  2825. if ( !WriteFile( hFile,
  2826. szSection,
  2827. _tcslen( szSection ) * sizeof(TCHAR),
  2828. &dwBytesWritten,
  2829. NULL ))
  2830. {
  2831. err = GetLastError();
  2832. goto cleanup;
  2833. }
  2834. for ( i = 0; i < nCountAttribTypes; i++ )
  2835. {
  2836. if ( !WriteFile( hFile,
  2837. aValuesAttribTypes[i],
  2838. _tcslen( aValuesAttribTypes[i] ) * sizeof(TCHAR),
  2839. &dwBytesWritten,
  2840. NULL ))
  2841. {
  2842. err = GetLastError();
  2843. goto cleanup;
  2844. }
  2845. if ( !WriteFile( hFile,
  2846. szEndOfLine,
  2847. sizeof(TCHAR),
  2848. &dwBytesWritten,
  2849. NULL ))
  2850. {
  2851. err = GetLastError();
  2852. goto cleanup;
  2853. }
  2854. }
  2855. _tcscpy( szSection, TEXT("[objectClasses]\n"));
  2856. if ( !WriteFile( hFile,
  2857. szSection,
  2858. _tcslen( szSection ) * sizeof(TCHAR),
  2859. &dwBytesWritten,
  2860. NULL ))
  2861. {
  2862. err = GetLastError();
  2863. goto cleanup;
  2864. }
  2865. for ( i = 0; i < nCountObjClasses; i++ )
  2866. {
  2867. if ( !WriteFile( hFile,
  2868. aValuesObjClasses[i],
  2869. _tcslen( aValuesObjClasses[i] ) * sizeof(TCHAR),
  2870. &dwBytesWritten,
  2871. NULL ))
  2872. {
  2873. err = GetLastError();
  2874. goto cleanup;
  2875. }
  2876. if ( !WriteFile( hFile,
  2877. szEndOfLine,
  2878. sizeof(TCHAR),
  2879. &dwBytesWritten,
  2880. NULL ))
  2881. {
  2882. err = GetLastError();
  2883. goto cleanup;
  2884. }
  2885. }
  2886. _tcscpy( szSection, TEXT("[DITContentRules]\n"));
  2887. if ( !WriteFile( hFile,
  2888. szSection,
  2889. _tcslen( szSection ) * sizeof(TCHAR),
  2890. &dwBytesWritten,
  2891. NULL ))
  2892. {
  2893. err = GetLastError();
  2894. goto cleanup;
  2895. }
  2896. for ( i = 0; i < nCountRules; i++ )
  2897. {
  2898. if ( !WriteFile( hFile,
  2899. aValuesRules[i],
  2900. _tcslen( aValuesRules[i] ) * sizeof(TCHAR),
  2901. &dwBytesWritten,
  2902. NULL ))
  2903. {
  2904. err = GetLastError();
  2905. goto cleanup;
  2906. }
  2907. if ( !WriteFile( hFile,
  2908. szEndOfLine,
  2909. sizeof(TCHAR),
  2910. &dwBytesWritten,
  2911. NULL ))
  2912. {
  2913. err = GetLastError();
  2914. goto cleanup;
  2915. }
  2916. }
  2917. cleanup:
  2918. CloseHandle( hFile );
  2919. return err;
  2920. }
  2921. #ifdef WIN95
  2922. DWORD StoreSchemaInfoToFileA(
  2923. LPSTR pszFile,
  2924. LPTSTR *aValuesAttribTypes,
  2925. int nCountAttribTypes,
  2926. LPTSTR *aValuesObjClasses,
  2927. int nCountObjClasses,
  2928. LPTSTR *aValuesRules,
  2929. int nCountRules
  2930. )
  2931. {
  2932. DWORD err = NO_ERROR;
  2933. HANDLE hFile = NULL;
  2934. hFile = CreateFileA( pszFile,
  2935. GENERIC_WRITE,
  2936. 0,
  2937. NULL,
  2938. CREATE_ALWAYS,
  2939. FILE_ATTRIBUTE_NORMAL,
  2940. NULL );
  2941. if ( hFile == INVALID_HANDLE_VALUE )
  2942. {
  2943. err = GetLastError();
  2944. if ( err == ERROR_PATH_NOT_FOUND )
  2945. {
  2946. //
  2947. // The directory is not created yet, create it now.
  2948. //
  2949. LPSTR pszTemp = strstr( pszFile, "SchCache\\");
  2950. pszTemp += strlen("SchCache");
  2951. *pszTemp = 0;
  2952. if ( !CreateDirectoryA( pszFile, NULL ))
  2953. return GetLastError();
  2954. *pszTemp = '\\';
  2955. hFile = CreateFileA( pszFile,
  2956. GENERIC_WRITE,
  2957. 0,
  2958. NULL,
  2959. CREATE_ALWAYS,
  2960. FILE_ATTRIBUTE_NORMAL,
  2961. NULL );
  2962. if ( hFile == INVALID_HANDLE_VALUE )
  2963. return GetLastError();
  2964. err = NO_ERROR;
  2965. }
  2966. else
  2967. {
  2968. return err;
  2969. }
  2970. }
  2971. err = StoreSchemaInfoToFileWithHandle(
  2972. hFile,
  2973. aValuesAttribTypes,
  2974. nCountAttribTypes,
  2975. aValuesObjClasses,
  2976. nCountObjClasses,
  2977. aValuesRules,
  2978. nCountRules
  2979. );
  2980. if ( err != NO_ERROR )
  2981. DeleteFileA( pszFile );
  2982. return err;
  2983. }
  2984. #endif
  2985. DWORD StoreSchemaInfoToFileW(
  2986. LPTSTR pszFile,
  2987. LPTSTR *aValuesAttribTypes,
  2988. int nCountAttribTypes,
  2989. LPTSTR *aValuesObjClasses,
  2990. int nCountObjClasses,
  2991. LPTSTR *aValuesRules,
  2992. int nCountRules
  2993. )
  2994. {
  2995. DWORD err = NO_ERROR;
  2996. HANDLE hFile = NULL;
  2997. hFile = CreateFile( pszFile,
  2998. GENERIC_WRITE,
  2999. 0,
  3000. NULL,
  3001. CREATE_ALWAYS,
  3002. FILE_ATTRIBUTE_NORMAL,
  3003. NULL );
  3004. if ( hFile == INVALID_HANDLE_VALUE )
  3005. {
  3006. err = GetLastError();
  3007. if ( err == ERROR_PATH_NOT_FOUND )
  3008. {
  3009. // The directory is not created yet, create it now.
  3010. LPTSTR pszTemp = _tcsrchr( pszFile, TEXT('\\'));
  3011. *pszTemp = 0;
  3012. if ( !CreateDirectory( pszFile, NULL ))
  3013. return GetLastError();
  3014. *pszTemp = TEXT('\\');
  3015. hFile = CreateFile( pszFile,
  3016. GENERIC_WRITE,
  3017. 0,
  3018. NULL,
  3019. CREATE_ALWAYS,
  3020. FILE_ATTRIBUTE_NORMAL,
  3021. NULL );
  3022. if ( hFile == INVALID_HANDLE_VALUE )
  3023. return GetLastError();
  3024. err = NO_ERROR;
  3025. }
  3026. else
  3027. {
  3028. return err;
  3029. }
  3030. }
  3031. err = StoreSchemaInfoToFileWithHandle(
  3032. hFile,
  3033. aValuesAttribTypes,
  3034. nCountAttribTypes,
  3035. aValuesObjClasses,
  3036. nCountObjClasses,
  3037. aValuesRules,
  3038. nCountRules
  3039. );
  3040. if ( err != NO_ERROR )
  3041. DeleteFile( pszFile );
  3042. return err;
  3043. }
  3044. DWORD ReadSchemaInfoFromRegistry(
  3045. HKEY hKey,
  3046. LPWSTR pszServer,
  3047. LPTSTR **paValuesAttribTypes,
  3048. int *pnCountAttribTypes,
  3049. LPTSTR **paValuesObjClasses,
  3050. int *pnCountObjClasses,
  3051. LPTSTR **paValuesRules,
  3052. int *pnCountRules,
  3053. LPBYTE *pBuffer
  3054. )
  3055. {
  3056. DWORD err = NO_ERROR;
  3057. LPTSTR pszPath = NULL;
  3058. LPTSTR pszExpandedPath = NULL;
  3059. LPSTR pszPathAscii = NULL;
  3060. LPSTR pszExpandedPathAscii = NULL;
  3061. LPTSTR pszTempPath = NULL;
  3062. DWORD dwLength = 0;
  3063. DWORD dwType;
  3064. //
  3065. // On chk bits would be good to make sure that this never
  3066. // happens as some schema stuff is messed up in this case.
  3067. //
  3068. if (!pszServer) {
  3069. ADsAssert(!"No server name was passed");
  3070. }
  3071. //
  3072. // Get the file name that is used to store the schema info
  3073. // from the registry.
  3074. //
  3075. err = RegQueryValueEx( hKey,
  3076. SCHEMA_FILE_NAME,
  3077. NULL,
  3078. &dwType,
  3079. NULL,
  3080. &dwLength );
  3081. #ifndef WIN95
  3082. if ( err )
  3083. goto cleanup;
  3084. #else
  3085. if (err == ERROR_MORE_DATA ) {
  3086. //
  3087. // Continue cause Win9x is dumb.
  3088. //
  3089. err = 0;
  3090. } else
  3091. goto cleanup;
  3092. #endif
  3093. pszPath = (LPTSTR) AllocADsMem( dwLength );
  3094. if ( pszPath == NULL )
  3095. return ERROR_NOT_ENOUGH_MEMORY;
  3096. err = RegQueryValueEx( hKey,
  3097. SCHEMA_FILE_NAME,
  3098. NULL,
  3099. &dwType,
  3100. (LPBYTE) pszPath,
  3101. &dwLength );
  3102. if ( err )
  3103. goto cleanup;
  3104. //
  3105. // Expand the path
  3106. //
  3107. pszExpandedPath = (LPTSTR) AllocADsMem( MAX_PATH * sizeof(WCHAR));
  3108. if ( pszExpandedPath == NULL )
  3109. {
  3110. err = ERROR_NOT_ENOUGH_MEMORY;
  3111. goto cleanup;
  3112. }
  3113. //
  3114. // At this point we want to rename all files called default.sch
  3115. // We look for default.sch in the string and then create a new
  3116. // string. For example if the string is %systemroot%\SCHEMA_DIR\
  3117. // Default.sch we will replace with %systemroot%\SCHEMA_DIR\
  3118. // pszServer.sch. This change will force schema to be dropped
  3119. // and we will end up picking up and storing the schema under the
  3120. // correct name. This will ensure that 2 different forests do
  3121. // not end up creating conflicting default.sch files that we
  3122. // never recover from internally. This excercise is futile unless
  3123. // a server name was passed in (should be the case always).
  3124. //
  3125. if (pszPath && *pszPath && pszServer) {
  3126. //
  3127. // Look for default.sch
  3128. //
  3129. pszTempPath = wcsstr( pszPath, DEFAULT_SCHEMA_FILE_NAME_WITH_EXT);
  3130. if (pszTempPath) {
  3131. //
  3132. // We now need to replace this string.
  3133. //
  3134. DWORD dwLenStr, dwLenNewPath;
  3135. dwLenStr = pszTempPath - pszPath;
  3136. //
  3137. // Now build the new string from the old one. Length is
  3138. // pszServer + the old piece upto schema file name.
  3139. //
  3140. dwLenNewPath = wcslen(pszServer)
  3141. + wcslen(SCHEMA_FILE_NAME_EXT)
  3142. + dwLenStr
  3143. + 1;
  3144. pszTempPath = (LPTSTR) AllocADsMem(dwLenNewPath * sizeof(WCHAR));
  3145. if (!pszTempPath) {
  3146. err = ERROR_NOT_ENOUGH_MEMORY;
  3147. goto cleanup;
  3148. }
  3149. wcsncpy(pszTempPath, pszPath, dwLenStr);
  3150. wcscat(pszTempPath, pszServer);
  3151. wcscat(pszTempPath, SCHEMA_FILE_NAME_EXT);
  3152. FreeADsMem(pszPath);
  3153. pszPath = pszTempPath;
  3154. pszTempPath = NULL;
  3155. //
  3156. // Now update the key in the registry. Ignore the error
  3157. // cause there is nothing we can really do about it.
  3158. //
  3159. err = RegSetValueEx(
  3160. hKey,
  3161. SCHEMA_FILE_NAME,
  3162. 0,
  3163. REG_EXPAND_SZ,
  3164. (CONST BYTE *) pszPath,
  3165. (_tcslen(pszPath) + 1 ) * sizeof(TCHAR)
  3166. );
  3167. }
  3168. }
  3169. dwLength = ExpandEnvironmentStrings( pszPath,
  3170. pszExpandedPath,
  3171. MAX_PATH );
  3172. #ifdef WIN95
  3173. //
  3174. // Just in case 3 bytes for each WCHAR rather than just 2.
  3175. //
  3176. pszExpandedPathAscii = (LPSTR) AllocADsMem( MAX_PATH * sizeof(char) * 3);
  3177. if (!pszExpandedPathAscii) {
  3178. err = ERROR_NOT_ENOUGH_MEMORY;
  3179. goto cleanup;
  3180. }
  3181. if (err = ConvertToAscii(pszPath, &pszPathAscii)) {
  3182. goto cleanup;
  3183. }
  3184. dwLength = ExpandEnvironmentStringsA(
  3185. pszPathAscii,
  3186. pszExpandedPathAscii,
  3187. MAX_PATH * sizeof(char) * 3
  3188. );
  3189. #endif
  3190. if ( dwLength == 0 )
  3191. {
  3192. err = GetLastError();
  3193. goto cleanup;
  3194. }
  3195. else if ( dwLength > MAX_PATH )
  3196. {
  3197. FreeADsMem( pszExpandedPath );
  3198. pszExpandedPath = (LPTSTR) AllocADsMem( dwLength * sizeof(WCHAR));
  3199. if ( pszExpandedPath == NULL )
  3200. {
  3201. err = ERROR_NOT_ENOUGH_MEMORY;
  3202. goto cleanup;
  3203. }
  3204. dwLength = ExpandEnvironmentStrings( pszPath,
  3205. pszExpandedPath,
  3206. dwLength );
  3207. if ( dwLength == 0 )
  3208. {
  3209. err = GetLastError();
  3210. goto cleanup;
  3211. }
  3212. }
  3213. //
  3214. // Now, read the info from the file
  3215. //
  3216. #ifndef WIN95
  3217. err = ReadSchemaInfoFromFileW(
  3218. pszExpandedPath,
  3219. paValuesAttribTypes,
  3220. pnCountAttribTypes,
  3221. paValuesObjClasses,
  3222. pnCountObjClasses,
  3223. paValuesRules,
  3224. pnCountRules,
  3225. pBuffer
  3226. );
  3227. #else
  3228. err = ReadSchemaInfoFromFileA(
  3229. pszExpandedPathAscii,
  3230. paValuesAttribTypes,
  3231. pnCountAttribTypes,
  3232. paValuesObjClasses,
  3233. pnCountObjClasses,
  3234. paValuesRules,
  3235. pnCountRules,
  3236. pBuffer
  3237. );
  3238. #endif
  3239. cleanup:
  3240. if ( pszPath )
  3241. FreeADsMem( pszPath );
  3242. if ( pszExpandedPath )
  3243. FreeADsMem( pszExpandedPath );
  3244. if (pszTempPath) {
  3245. FreeADsMem(pszTempPath);
  3246. }
  3247. #ifdef WIN95
  3248. if (pszPathAscii) {
  3249. FreeADsMem(pszPathAscii);
  3250. }
  3251. if (pszExpandedPathAscii) {
  3252. FreeADsMem(pszExpandedPathAscii);
  3253. }
  3254. #endif
  3255. return err;
  3256. }
  3257. DWORD StoreSchemaInfoInRegistry(
  3258. HKEY hKey,
  3259. LPTSTR pszServer,
  3260. LPTSTR pszTime,
  3261. LPTSTR *aValuesAttribTypes,
  3262. int nCountAttribTypes,
  3263. LPTSTR *aValuesObjClasses,
  3264. int nCountObjClasses,
  3265. LPTSTR *aValuesRules,
  3266. int nCountRules,
  3267. BOOL fProcessAUX
  3268. )
  3269. {
  3270. DWORD err = NO_ERROR;
  3271. LPTSTR pszPath = NULL;
  3272. LPTSTR pszExpandedPath = NULL;
  3273. LPSTR pszPathAscii = NULL;
  3274. LPSTR pszExpandedPathAscii = NULL;
  3275. DWORD dwLength = 0;
  3276. DWORD dwType;
  3277. DWORD dwProcessAUX;
  3278. //
  3279. // See if we can find the file name that is used to store the schema info
  3280. // in the registry.
  3281. //
  3282. err = RegQueryValueEx( hKey,
  3283. SCHEMA_FILE_NAME,
  3284. NULL,
  3285. &dwType,
  3286. NULL,
  3287. &dwLength );
  3288. if ( err == NO_ERROR )
  3289. {
  3290. pszPath = (LPTSTR) AllocADsMem( dwLength );
  3291. if ( pszPath == NULL )
  3292. return ERROR_NOT_ENOUGH_MEMORY;
  3293. err = RegQueryValueEx( hKey,
  3294. SCHEMA_FILE_NAME,
  3295. NULL,
  3296. &dwType,
  3297. (LPBYTE) pszPath,
  3298. &dwLength );
  3299. if ( err )
  3300. goto cleanup;
  3301. }
  3302. err = NO_ERROR;
  3303. if ( pszPath == NULL ) // Cannot read from the registry
  3304. {
  3305. //
  3306. // Allocate pszPath to be either MAX_PATH chars or sufficient
  3307. // to hold %SystemRoot%/<SCHEMA_DIR_NAME>/<pszServer>.sch, whichever
  3308. // is larger.
  3309. //
  3310. if (pszServer) {
  3311. DWORD cbPath = (MAX_PATH > (_tcslen(TEXT("%SystemRoot%\\")) +
  3312. _tcslen(SCHEMA_DIR_NAME) +
  3313. _tcslen(pszServer) +
  3314. _tcslen(SCHEMA_FILE_NAME_EXT))) ?
  3315. (MAX_PATH * sizeof(WCHAR)) :
  3316. (2 * _tcslen(pszServer) * sizeof(WCHAR));
  3317. pszPath = (LPTSTR) AllocADsMem(cbPath);
  3318. }
  3319. else{
  3320. //
  3321. // pszServe can be NULL in the ldapc layer users case
  3322. //
  3323. pszPath = (LPTSTR) AllocADsMem(MAX_PATH * sizeof(WCHAR));
  3324. }
  3325. if ( pszPath == NULL )
  3326. return ERROR_NOT_ENOUGH_MEMORY;
  3327. //
  3328. // Build the path of the schema info file
  3329. //
  3330. #ifndef WIN95
  3331. _tcscpy( pszPath, TEXT("%SystemRoot%\\"));
  3332. #else
  3333. _tcscpy( pszPath, TEXT("%WINDIR%\\"));
  3334. #endif
  3335. _tcscat( pszPath, SCHEMA_DIR_NAME);
  3336. if (pszServer) {
  3337. //
  3338. // Server strings may have a port number in them,
  3339. // e.g., "ntdev:389". We need to change this to
  3340. // "ntdev_389", otherwise the colon will give us trouble
  3341. // in the filename.
  3342. //
  3343. LPTSTR pszColon = _tcschr(pszServer, (TCHAR)':');
  3344. if (!pszColon) {
  3345. _tcscat( pszPath, pszServer);
  3346. }
  3347. else {
  3348. _tcsncat( pszPath, pszServer, pszColon-pszServer);
  3349. _tcscat ( pszPath, TEXT("_"));
  3350. _tcscat ( pszPath, pszColon+1); // the number after the colon
  3351. }
  3352. }
  3353. else {
  3354. _tcscat( pszPath, DEFAULT_SCHEMA_FILE_NAME);
  3355. }
  3356. _tcscat( pszPath, SCHEMA_FILE_NAME_EXT );
  3357. }
  3358. pszExpandedPath = (LPTSTR) AllocADsMem( MAX_PATH * sizeof(WCHAR) );
  3359. if ( pszExpandedPath == NULL )
  3360. {
  3361. err = ERROR_NOT_ENOUGH_MEMORY;
  3362. goto cleanup;
  3363. }
  3364. dwLength = ExpandEnvironmentStrings( pszPath,
  3365. pszExpandedPath,
  3366. MAX_PATH );
  3367. #ifdef WIN95
  3368. pszExpandedPathAscii = (LPSTR) AllocADsMem(MAX_PATH * sizeof(char) * 3);
  3369. if (!pszExpandedPathAscii) {
  3370. err = ERROR_NOT_ENOUGH_MEMORY;
  3371. goto cleanup;
  3372. }
  3373. if (err = ConvertToAscii(pszPath, &pszPathAscii)) {
  3374. goto cleanup;
  3375. }
  3376. dwLength = ExpandEnvironmentStringsA(
  3377. pszPathAscii,
  3378. pszExpandedPathAscii,
  3379. MAX_PATH * sizeof(char) * 3
  3380. );
  3381. #endif
  3382. if ( dwLength == 0 )
  3383. {
  3384. err = GetLastError();
  3385. goto cleanup;
  3386. }
  3387. else if ( dwLength > MAX_PATH )
  3388. {
  3389. FreeADsMem( pszExpandedPath );
  3390. pszExpandedPath = (LPTSTR) AllocADsMem( dwLength * sizeof(WCHAR) );
  3391. if ( pszExpandedPath == NULL )
  3392. {
  3393. err = ERROR_NOT_ENOUGH_MEMORY;
  3394. goto cleanup;
  3395. }
  3396. dwLength = ExpandEnvironmentStrings( pszPath,
  3397. pszExpandedPath,
  3398. dwLength );
  3399. if ( dwLength == 0 )
  3400. {
  3401. err = GetLastError();
  3402. goto cleanup;
  3403. }
  3404. }
  3405. //
  3406. // Write the schema information into the file
  3407. //
  3408. #ifndef WIN95
  3409. err = StoreSchemaInfoToFileW(
  3410. pszExpandedPath,
  3411. aValuesAttribTypes,
  3412. nCountAttribTypes,
  3413. aValuesObjClasses,
  3414. nCountObjClasses,
  3415. aValuesRules,
  3416. nCountRules
  3417. );
  3418. #else
  3419. err = StoreSchemaInfoToFileA(
  3420. pszExpandedPathAscii,
  3421. aValuesAttribTypes,
  3422. nCountAttribTypes,
  3423. aValuesObjClasses,
  3424. nCountObjClasses,
  3425. aValuesRules,
  3426. nCountRules
  3427. );
  3428. #endif
  3429. if ( err )
  3430. goto cleanup;
  3431. //
  3432. // Write the information into the registry
  3433. //
  3434. err = RegSetValueEx( hKey,
  3435. SCHEMA_FILE_NAME,
  3436. 0,
  3437. REG_EXPAND_SZ,
  3438. (CONST BYTE *) pszPath,
  3439. (_tcslen(pszPath) + 1 ) * sizeof(TCHAR));
  3440. if ( err )
  3441. goto cleanup;
  3442. err = RegSetValueEx( hKey,
  3443. SCHEMA_TIME,
  3444. 0,
  3445. REG_SZ,
  3446. (CONST BYTE *) pszTime,
  3447. (_tcslen(pszTime) + 1 ) * sizeof(TCHAR));
  3448. if ( err )
  3449. goto cleanup;
  3450. dwProcessAUX = (TRUE == fProcessAUX) ? 1: 0;
  3451. err = RegSetValueExW( hKey,
  3452. SCHEMA_PROCESSAUX,
  3453. 0,
  3454. REG_DWORD,
  3455. (CONST BYTE *) &dwProcessAUX,
  3456. sizeof(dwProcessAUX));
  3457. cleanup:
  3458. if ( pszPath )
  3459. FreeADsMem( pszPath );
  3460. if ( pszExpandedPath )
  3461. FreeADsMem( pszExpandedPath );
  3462. #ifdef WIN95
  3463. if (pszPathAscii) {
  3464. FreeADsMem(pszPathAscii);
  3465. }
  3466. if (pszExpandedPathAscii) {
  3467. FreeADsMem(pszExpandedPathAscii);
  3468. }
  3469. #endif
  3470. return err;
  3471. }
  3472. //+---------------------------------------------------------------------------
  3473. // Function: AttributeTypeDescription
  3474. //
  3475. // Synopsis: Parses an attribute type description.
  3476. // It parses the following grammar rules
  3477. //
  3478. // <AttributeTypeDescription> ::= "("
  3479. // <oid> -- AttributeType identifier
  3480. // [ "NAME" <DirectoryStrings> ] -- name used in AttributeType
  3481. // [ "DESC" <DirectoryString> ]
  3482. // [ "OBSOLETE" ]
  3483. // [ "SUP" <oid> ] -- derived from this other AttributeType
  3484. // [ "EQUALITY" <oid> ] -- Matching Rule name
  3485. // [ "ORDERING" <oid> ] -- Matching Rule name
  3486. // [ "SUBSTR" <oid> ] -- Matching Rule name
  3487. // [ "SYNTAX" <DirectoryString> ] -- see section 4.2
  3488. // [ "SINGLE-VALUE" ] -- default multi-valued
  3489. // [ "COLLECTIVE" ] -- default not collective
  3490. // [ "DYNAMIC" ] -- default not dynamic
  3491. // [ "NO-USER-MODIFICATION" ] -- default user modifiable
  3492. // [ "USAGE" <AttributeUsage> ] -- default user applications
  3493. // ")"
  3494. //
  3495. // <AttributeUsage> ::=
  3496. // "userApplications"
  3497. // | "directoryOperation"
  3498. // | "distributedOperation" -- DSA-shared
  3499. // | "dSAOperation" -- DSA-specific, value depends on server
  3500. //
  3501. //
  3502. // Arguments: [LPTSTR] pszAttrType : The string to parse
  3503. //
  3504. // Returns: [HRESULT] 0 if successful, error HRESULT if not
  3505. //
  3506. // Modifies: pTokenizer (consumes the input buffer)
  3507. //
  3508. // History: 9-3-96 yihsins Created.
  3509. //
  3510. //----------------------------------------------------------------------------
  3511. HRESULT
  3512. AttributeTypeDescription(
  3513. LPTSTR pszAttrType,
  3514. PPROPERTYINFO pPropertyInfo,
  3515. LPWSTR **pppszNames,
  3516. PDWORD pdwNameCount
  3517. )
  3518. {
  3519. TCHAR szToken[MAX_TOKEN_LENGTH];
  3520. DWORD dwToken;
  3521. HRESULT hr;
  3522. CSchemaLexer Tokenizer;
  3523. hr = Tokenizer.InitializePath(pszAttrType);
  3524. BAIL_IF_ERROR(hr);
  3525. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3526. BAIL_IF_ERROR(hr);
  3527. if ( dwToken != TOKEN_OPENBRACKET )
  3528. RRETURN(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
  3529. //
  3530. // use TRUE flag as there is a chance that from
  3531. // some schemas, we get bad data that has no GUID
  3532. //
  3533. hr = Oid( &Tokenizer, &(pPropertyInfo->pszOID), TRUE);
  3534. BAIL_IF_ERROR(hr);
  3535. while ( TRUE ) {
  3536. LPWSTR *ppszDirStrings;
  3537. DWORD dwCount,dwCtr;
  3538. ppszDirStrings = NULL;
  3539. dwCount = 0;
  3540. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3541. BAIL_IF_ERROR(hr);
  3542. if ( dwToken == TOKEN_IDENTIFIER )
  3543. Tokenizer.IsKeyword( szToken, &dwToken );
  3544. switch ( dwToken ) {
  3545. case TOKEN_CLOSEBRACKET:
  3546. RRETURN(S_OK);
  3547. case TOKEN_NAME:
  3548. hr = DirectoryStrings(
  3549. &Tokenizer,
  3550. &ppszDirStrings,
  3551. &dwCount
  3552. );
  3553. BAIL_IF_ERROR(hr);
  3554. if (!ppszDirStrings) {
  3555. //
  3556. // We need at least one name.
  3557. //
  3558. BAIL_IF_ERROR(
  3559. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
  3560. );
  3561. }
  3562. //
  3563. // For now we will only support the first name in the list.
  3564. //
  3565. pPropertyInfo->pszPropertyName = ppszDirStrings[0];
  3566. //
  3567. // The remaining values if any will require additional
  3568. // processing in FillPropertyInfoArray.
  3569. //
  3570. *pppszNames = ppszDirStrings;
  3571. *pdwNameCount = dwCount;
  3572. break;
  3573. case TOKEN_DESC:
  3574. hr = DirectoryString( &Tokenizer,
  3575. &(pPropertyInfo->pszDescription));
  3576. break;
  3577. case TOKEN_OBSOLETE:
  3578. // attribute is obsolete (RFC 2252)
  3579. pPropertyInfo->fObsolete = TRUE;
  3580. break;
  3581. case TOKEN_SUP:
  3582. hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDSup));
  3583. break;
  3584. case TOKEN_EQUALITY:
  3585. hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDEquality));
  3586. break;
  3587. case TOKEN_ORDERING:
  3588. hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDOrdering));
  3589. break;
  3590. case TOKEN_SUBSTR:
  3591. hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDSubstr));
  3592. break;
  3593. case TOKEN_SYNTAX:
  3594. hr = DirectoryString( &Tokenizer, &(pPropertyInfo->pszSyntax));
  3595. //
  3596. // It need not necessarily be a DirectoryString can also be
  3597. // an OID. So if DirectoryString fails, we should try OID.
  3598. //
  3599. if (FAILED(hr)
  3600. && (hr == HRESULT_FROM_WIN32(ERROR_INVALID_DATA))
  3601. ) {
  3602. Tokenizer.PushBackToken();
  3603. hr = Oid( &Tokenizer, &(pPropertyInfo->pszSyntax));
  3604. }
  3605. break;
  3606. case TOKEN_SINGLE_VALUE:
  3607. pPropertyInfo->fSingleValued = TRUE;
  3608. break;
  3609. case TOKEN_COLLECTIVE:
  3610. pPropertyInfo->fCollective = TRUE;
  3611. break;
  3612. case TOKEN_DYNAMIC:
  3613. pPropertyInfo->fDynamic = TRUE;
  3614. break;
  3615. case TOKEN_NO_USER_MODIFICATION:
  3616. pPropertyInfo->fNoUserModification = TRUE;
  3617. break;
  3618. case TOKEN_USAGE:
  3619. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3620. BAIL_IF_ERROR(hr);
  3621. if (_tcsicmp(szToken, TEXT("userApplications")) == 0)
  3622. pPropertyInfo->dwUsage = ATTR_USAGE_USERAPPLICATIONS;
  3623. else if (_tcsicmp(szToken, TEXT("directoryOperation")) == 0)
  3624. pPropertyInfo->dwUsage = ATTR_USAGE_DIRECTORYOPERATION;
  3625. else if (_tcsicmp(szToken, TEXT("distributedOperation")) == 0)
  3626. pPropertyInfo->dwUsage = ATTR_USAGE_DISTRIBUTEDOPERATION;
  3627. else if (_tcsicmp(szToken, TEXT("dSAOperation")) == 0)
  3628. pPropertyInfo->dwUsage = ATTR_USAGE_DSAOPERATION;
  3629. break;
  3630. case TOKEN_OPEN_CURLY :
  3631. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3632. BAIL_IF_ERROR(hr);
  3633. if (dwToken != TOKEN_IDENTIFIER) {
  3634. BAIL_IF_ERROR(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
  3635. }
  3636. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3637. BAIL_IF_ERROR(hr);
  3638. if (dwToken != TOKEN_CLOSE_CURLY) {
  3639. BAIL_IF_ERROR(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
  3640. }
  3641. break;
  3642. case TOKEN_X :
  3643. //
  3644. // This means that this token and the following
  3645. // DirectoryStrings token (which can be empty string)
  3646. // need to be ignored.
  3647. //
  3648. hr = DirectoryStrings(
  3649. &Tokenizer,
  3650. &ppszDirStrings,
  3651. &dwCount
  3652. );
  3653. //
  3654. // If we could not process this then we need to BAIL
  3655. // as the Tokenizer is not in a recoverable state.
  3656. //
  3657. BAIL_IF_ERROR(hr);
  3658. //
  3659. // Free the strings that came back.
  3660. //
  3661. FreeDirectoryStrings(
  3662. ppszDirStrings,
  3663. dwCount
  3664. );
  3665. ppszDirStrings = NULL;
  3666. break;
  3667. default:
  3668. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  3669. break;
  3670. }
  3671. BAIL_IF_ERROR(hr);
  3672. }
  3673. cleanup:
  3674. RRETURN(hr);
  3675. }
  3676. //+---------------------------------------------------------------------------
  3677. // Function: ObjectClassDescription
  3678. //
  3679. // Synopsis: Parses an object class description.
  3680. // It parses the following grammar rules
  3681. //
  3682. // <ObjectClassDescription> ::= "("
  3683. // <oid> -- ObjectClass identifier
  3684. // [ "NAME" <DirectoryStrings> ]
  3685. // [ "DESC" <DirectoryString> ]
  3686. // [ "OBSOLETE" ]
  3687. // [ "SUP" <oids> ] -- Superior ObjectClasses
  3688. // [ ( "ABSTRACT" | "STRUCTURAL" | "AUXILIARY" )] -- default structural
  3689. // [ "MUST" <oids> ] -- AttributeTypes
  3690. // [ "MAY" <oids> ] -- AttributeTypes
  3691. // ")"
  3692. //
  3693. // Arguments: [LPTSTR] pszObjectClass : The string to parse
  3694. //
  3695. // Returns: [HRESULT] 0 if successful, error HRESULT if not
  3696. //
  3697. // Modifies: pTokenizer (consumes the input buffer)
  3698. //
  3699. // History: 9-3-96 yihsins Created.
  3700. //
  3701. //----------------------------------------------------------------------------
  3702. HRESULT
  3703. ObjectClassDescription(
  3704. LPTSTR pszObjectClass,
  3705. PCLASSINFO pClassInfo,
  3706. SEARCHENTRY *aPropSearchTable,
  3707. DWORD dwSearchTableCount,
  3708. LPWSTR **pppszNewNames,
  3709. PDWORD pdwNameCount
  3710. )
  3711. {
  3712. TCHAR szToken[MAX_TOKEN_LENGTH];
  3713. LPWSTR pszTemp;
  3714. DWORD dwToken;
  3715. HRESULT hr;
  3716. CSchemaLexer Tokenizer;
  3717. hr = Tokenizer.InitializePath(pszObjectClass);
  3718. BAIL_IF_ERROR(hr);
  3719. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3720. BAIL_IF_ERROR(hr);
  3721. if ( dwToken != TOKEN_OPENBRACKET )
  3722. RRETURN(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
  3723. //
  3724. // use TRUE flag as there is a chance that from
  3725. // some schemas, we get bad data that has no GUID
  3726. //
  3727. hr = Oid( &Tokenizer, &(pClassInfo->pszOID), TRUE);
  3728. BAIL_IF_ERROR(hr);
  3729. while ( TRUE ) {
  3730. LPWSTR *ppszDirStrings;
  3731. DWORD dwCount,dwCtr;
  3732. ppszDirStrings = NULL;
  3733. dwCount = 0;
  3734. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3735. BAIL_IF_ERROR(hr);
  3736. if ( dwToken == TOKEN_IDENTIFIER )
  3737. Tokenizer.IsKeyword( szToken, &dwToken );
  3738. switch ( dwToken ) {
  3739. case TOKEN_CLOSEBRACKET:
  3740. RRETURN(S_OK);
  3741. case TOKEN_NAME:
  3742. hr = DirectoryStrings(
  3743. &Tokenizer,
  3744. &ppszDirStrings,
  3745. &dwCount
  3746. );
  3747. BAIL_IF_ERROR(hr);
  3748. if (!ppszDirStrings) {
  3749. //
  3750. // We need at least one name.
  3751. //
  3752. BAIL_IF_ERROR(
  3753. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
  3754. );
  3755. }
  3756. //
  3757. // For now we will only support the first name in the list.
  3758. //
  3759. pClassInfo->pszName = ppszDirStrings[0];
  3760. //
  3761. // The remaining strings will need additional processing
  3762. // in fillClassInfoArray
  3763. //
  3764. *pppszNewNames = ppszDirStrings;
  3765. *pdwNameCount = dwCount;
  3766. break;
  3767. case TOKEN_DESC:
  3768. hr = DirectoryString(&Tokenizer,&(pClassInfo->pszDescription));
  3769. break;
  3770. case TOKEN_OBSOLETE:
  3771. // class is obsolete (RFC 2252)
  3772. pClassInfo->fObsolete = TRUE;
  3773. break;
  3774. case TOKEN_SUP:
  3775. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3776. BAIL_IF_ERROR(hr);
  3777. Tokenizer.PushBackToken();
  3778. if ( dwToken == TOKEN_QUOTE )
  3779. {
  3780. DWORD dwNumStrings = 0;
  3781. LPWSTR *ppszTmp = NULL;
  3782. while (dwToken == TOKEN_QUOTE) {
  3783. hr = DirectoryString( &Tokenizer,
  3784. &(pszTemp));
  3785. BAIL_IF_ERROR(hr);
  3786. if (dwNumStrings == 0) {
  3787. pClassInfo->pOIDsSuperiorClasses
  3788. = (LPWSTR *) AllocADsMem(sizeof(LPWSTR) * 2);
  3789. } else {
  3790. ppszTmp
  3791. = (LPWSTR *)
  3792. ReallocADsMem(
  3793. pClassInfo->pOIDsSuperiorClasses,
  3794. sizeof(LPWSTR) * (dwNumStrings + 1),
  3795. sizeof(LPWSTR) * (dwNumStrings + 2)
  3796. );
  3797. if(!ppszTmp)
  3798. {
  3799. BAIL_IF_ERROR(hr = E_OUTOFMEMORY);
  3800. }
  3801. pClassInfo->pOIDsSuperiorClasses = ppszTmp;
  3802. ppszTmp = NULL;
  3803. }
  3804. if ( pClassInfo->pOIDsSuperiorClasses == NULL )
  3805. {
  3806. hr = E_OUTOFMEMORY;
  3807. BAIL_IF_ERROR(hr);
  3808. }
  3809. pClassInfo->pOIDsSuperiorClasses[dwNumStrings] = pszTemp;
  3810. pClassInfo->pOIDsSuperiorClasses[++dwNumStrings] = NULL;
  3811. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3812. BAIL_IF_ERROR(hr);
  3813. Tokenizer.PushBackToken();
  3814. } // while
  3815. } // the token was not a quote
  3816. else {
  3817. hr = Oids(&Tokenizer, &(pClassInfo->pOIDsSuperiorClasses),NULL);
  3818. }
  3819. break;
  3820. case TOKEN_ABSTRACT:
  3821. pClassInfo->dwType = CLASS_TYPE_ABSTRACT;
  3822. break;
  3823. case TOKEN_STRUCTURAL:
  3824. pClassInfo->dwType = CLASS_TYPE_STRUCTURAL;
  3825. break;
  3826. case TOKEN_AUXILIARY:
  3827. pClassInfo->dwType = CLASS_TYPE_AUXILIARY;
  3828. break;
  3829. case TOKEN_MUST:
  3830. hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMustContain),
  3831. &(pClassInfo->nNumOfMustContain),
  3832. aPropSearchTable, dwSearchTableCount );
  3833. break;
  3834. case TOKEN_MAY:
  3835. hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMayContain),
  3836. &(pClassInfo->nNumOfMayContain),
  3837. aPropSearchTable, dwSearchTableCount );
  3838. break;
  3839. case TOKEN_X:
  3840. //
  3841. // This is provider specific info - parse and ignore.
  3842. //
  3843. hr = DirectoryStrings(
  3844. &Tokenizer,
  3845. &ppszDirStrings,
  3846. &dwCount
  3847. );
  3848. //
  3849. // If we could not process this then we need to BAIL
  3850. // as the Tokenizer is not in a recoverable state.
  3851. //
  3852. BAIL_IF_ERROR(hr);
  3853. if (ppszDirStrings) {
  3854. FreeDirectoryStrings(
  3855. ppszDirStrings,
  3856. dwCount
  3857. );
  3858. ppszDirStrings = NULL;
  3859. }
  3860. break;
  3861. default:
  3862. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  3863. break;
  3864. }
  3865. BAIL_IF_ERROR(hr);
  3866. }
  3867. cleanup:
  3868. RRETURN(hr);
  3869. }
  3870. //+---------------------------------------------------------------------------
  3871. // Function: DITContentRuleDescription
  3872. //
  3873. // Synopsis: Parses an DIT content rule description.
  3874. // It parses the following grammar rules
  3875. //
  3876. // <DITContentDescription> ::= "("
  3877. // <oid> -- ObjectClass identifier
  3878. // [ "NAME" <DirectoryStrings> ]
  3879. // [ "DESC" <DirectoryString> ]
  3880. // [ "OBSOLETE" ]
  3881. // [ "AUX" <oids> ] -- Auxiliary ObjectClasses
  3882. // [ "MUST" <oids> ] -- AttributeTypes
  3883. // [ "MAY" <oids> ] -- AttributeTypes
  3884. // [ "NOT" <oids> ] -- AttributeTypes
  3885. // ")"
  3886. //
  3887. // Arguments: [LPTSTR] pszObjectClass : The string to parse
  3888. //
  3889. // Returns: [HRESULT] 0 if successful, error HRESULT if not
  3890. //
  3891. // Modifies: pTokenizer (consumes the input buffer)
  3892. //
  3893. // History: 9-3-96 yihsins Created.
  3894. //
  3895. //----------------------------------------------------------------------------
  3896. HRESULT
  3897. DITContentRuleDescription( LPTSTR pszObjectClass, PCLASSINFO pClassInfo,
  3898. SEARCHENTRY *aPropSearchTable,
  3899. DWORD dwSearchTableCount )
  3900. {
  3901. TCHAR szToken[MAX_TOKEN_LENGTH];
  3902. DWORD dwToken;
  3903. HRESULT hr;
  3904. CSchemaLexer Tokenizer;
  3905. hr = Tokenizer.InitializePath(pszObjectClass);
  3906. BAIL_IF_ERROR(hr);
  3907. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3908. BAIL_IF_ERROR(hr);
  3909. if ( dwToken != TOKEN_OPENBRACKET )
  3910. RRETURN(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
  3911. hr = Oid( &Tokenizer, &(pClassInfo->pszOID));
  3912. BAIL_IF_ERROR(hr);
  3913. while ( TRUE ) {
  3914. hr = Tokenizer.GetNextToken(szToken, &dwToken);
  3915. BAIL_IF_ERROR(hr);
  3916. if ( dwToken == TOKEN_IDENTIFIER )
  3917. Tokenizer.IsKeyword( szToken, &dwToken );
  3918. switch ( dwToken ) {
  3919. case TOKEN_CLOSEBRACKET:
  3920. RRETURN(S_OK);
  3921. case TOKEN_NAME:
  3922. hr = DirectoryString( &Tokenizer, NULL);
  3923. // DirectoryStrings
  3924. break;
  3925. case TOKEN_DESC:
  3926. hr = DirectoryString( &Tokenizer, NULL);
  3927. break;
  3928. case TOKEN_OBSOLETE:
  3929. // rule is obsolete (RFC 2252)
  3930. pClassInfo->fObsolete = TRUE;
  3931. break;
  3932. case TOKEN_AUX:
  3933. hr = Oids(&Tokenizer, &(pClassInfo->pOIDsAuxClasses), NULL);
  3934. break;
  3935. case TOKEN_MUST:
  3936. hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMustContain),
  3937. &(pClassInfo->nNumOfMustContain),
  3938. aPropSearchTable, dwSearchTableCount );
  3939. break;
  3940. case TOKEN_MAY:
  3941. hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMayContain),
  3942. &(pClassInfo->nNumOfMayContain),
  3943. aPropSearchTable, dwSearchTableCount );
  3944. break;
  3945. case TOKEN_NOT:
  3946. hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsNotContain),
  3947. &(pClassInfo->nNumOfNotContain),
  3948. aPropSearchTable, dwSearchTableCount );
  3949. break;
  3950. default:
  3951. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  3952. break;
  3953. }
  3954. BAIL_IF_ERROR(hr);
  3955. }
  3956. cleanup:
  3957. RRETURN(hr);
  3958. }
  3959. HRESULT
  3960. Oid(CSchemaLexer * pTokenizer, LPTSTR *ppszOID, BOOL fNoGuid )
  3961. {
  3962. TCHAR szToken[MAX_TOKEN_LENGTH];
  3963. DWORD dwToken;
  3964. HRESULT hr;
  3965. *ppszOID = NULL;
  3966. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  3967. BAIL_IF_ERROR(hr);
  3968. if ( dwToken != TOKEN_IDENTIFIER )
  3969. {
  3970. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  3971. BAIL_IF_ERROR(hr);
  3972. }
  3973. //
  3974. // Since some people do not like to have
  3975. // an OID on all attributes, we need to work around them.
  3976. // This should be changed once all schemas are compliant
  3977. // AjayR 11-12-98.
  3978. //
  3979. if (fNoGuid && _wcsicmp(szToken, L"NAME") == 0) {
  3980. *ppszOID = AllocADsStr(L"");
  3981. pTokenizer->PushBackToken();
  3982. } else
  3983. *ppszOID = AllocADsStr( szToken );
  3984. if ( *ppszOID == NULL )
  3985. {
  3986. hr = E_OUTOFMEMORY;
  3987. BAIL_IF_ERROR(hr);
  3988. }
  3989. cleanup:
  3990. if ( FAILED(hr))
  3991. {
  3992. if ( *ppszOID )
  3993. {
  3994. FreeADsStr( *ppszOID );
  3995. *ppszOID = NULL;
  3996. }
  3997. }
  3998. RRETURN(hr);
  3999. }
  4000. HRESULT
  4001. Oids(CSchemaLexer * pTokenizer, LPTSTR **pOIDs, DWORD *pnNumOfOIDs )
  4002. {
  4003. TCHAR szToken[MAX_TOKEN_LENGTH];
  4004. DWORD dwToken;
  4005. HRESULT hr;
  4006. DWORD nCount = 0;
  4007. DWORD nCurrent = 0;
  4008. LPTSTR * pTemp = NULL;
  4009. *pOIDs = NULL;
  4010. if ( pnNumOfOIDs )
  4011. *pnNumOfOIDs = 0;
  4012. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4013. BAIL_IF_ERROR(hr);
  4014. if ( dwToken == TOKEN_IDENTIFIER )
  4015. {
  4016. // All classes are subclasses of "top", and hence must contain
  4017. // "objectClass" attribute. Add the "objectClass" attribute here
  4018. // to prevent processing later.
  4019. nCount = 2;
  4020. *pOIDs = (LPTSTR *) AllocADsMem( sizeof(LPTSTR) * nCount);
  4021. if ( *pOIDs == NULL )
  4022. {
  4023. hr = E_OUTOFMEMORY;
  4024. BAIL_IF_ERROR(hr);
  4025. }
  4026. (*pOIDs)[nCurrent] = AllocADsStr( szToken );
  4027. if ( (*pOIDs)[nCurrent] == NULL )
  4028. {
  4029. hr = E_OUTOFMEMORY;
  4030. BAIL_IF_ERROR(hr);
  4031. }
  4032. (*pOIDs)[++nCurrent] = NULL;
  4033. }
  4034. else if ( dwToken == TOKEN_OPENBRACKET )
  4035. {
  4036. nCount = 10;
  4037. *pOIDs = (LPTSTR *) AllocADsMem( sizeof(LPTSTR) * nCount);
  4038. if ( *pOIDs == NULL )
  4039. {
  4040. hr = E_OUTOFMEMORY;
  4041. BAIL_IF_ERROR(hr);
  4042. }
  4043. do {
  4044. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4045. BAIL_IF_ERROR(hr);
  4046. if ( dwToken == TOKEN_IDENTIFIER )
  4047. {
  4048. if ( nCurrent == nCount )
  4049. {
  4050. pTemp = (LPTSTR *) ReallocADsMem( *pOIDs,
  4051. sizeof(LPTSTR) * nCount,
  4052. sizeof(LPTSTR) * nCount * 2);
  4053. if ( pTemp == NULL )
  4054. {
  4055. hr = E_OUTOFMEMORY;
  4056. BAIL_IF_ERROR(hr);
  4057. }
  4058. *pOIDs = pTemp;
  4059. pTemp = NULL;
  4060. nCount *= 2;
  4061. }
  4062. (*pOIDs)[nCurrent] = AllocADsStr( szToken );
  4063. if ( (*pOIDs)[nCurrent] == NULL )
  4064. {
  4065. hr = E_OUTOFMEMORY;
  4066. BAIL_IF_ERROR(hr);
  4067. }
  4068. nCurrent++;
  4069. }
  4070. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4071. BAIL_IF_ERROR(hr);
  4072. } while ( dwToken == TOKEN_DOLLAR );
  4073. if ( dwToken != TOKEN_CLOSEBRACKET )
  4074. {
  4075. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  4076. BAIL_IF_ERROR(hr);
  4077. }
  4078. if ( nCurrent == nCount )
  4079. {
  4080. // Need one extra NULL entry at the end of the array
  4081. pTemp = (LPTSTR *) ReallocADsMem( *pOIDs,
  4082. sizeof(LPTSTR) * nCount,
  4083. sizeof(LPTSTR) * (nCount + 1));
  4084. if ( pTemp == NULL )
  4085. {
  4086. hr = E_OUTOFMEMORY;
  4087. BAIL_IF_ERROR(hr);
  4088. }
  4089. *pOIDs = pTemp;
  4090. pTemp = NULL;
  4091. nCount += 1;
  4092. }
  4093. }
  4094. else
  4095. {
  4096. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  4097. BAIL_IF_ERROR(hr);
  4098. }
  4099. if ( pnNumOfOIDs )
  4100. *pnNumOfOIDs = nCurrent;
  4101. cleanup:
  4102. if ( FAILED(hr))
  4103. {
  4104. if ( *pOIDs )
  4105. {
  4106. for ( DWORD i = 0; i < nCount; i++ )
  4107. {
  4108. if ( (*pOIDs)[i] )
  4109. FreeADsStr( (*pOIDs)[i] );
  4110. }
  4111. FreeADsMem( *pOIDs );
  4112. *pOIDs = NULL;
  4113. }
  4114. }
  4115. RRETURN(hr);
  4116. }
  4117. HRESULT
  4118. PropOids(CSchemaLexer * pTokenizer, int **pOIDs, DWORD *pnNumOfOIDs,
  4119. SEARCHENTRY *aPropSearchTable, DWORD dwSearchTableCount )
  4120. {
  4121. TCHAR szToken[MAX_TOKEN_LENGTH];
  4122. DWORD dwToken;
  4123. HRESULT hr;
  4124. DWORD nCount = 0;
  4125. DWORD nCurrent = 0;
  4126. int * pTemp = NULL;
  4127. *pOIDs = NULL;
  4128. if ( pnNumOfOIDs )
  4129. *pnNumOfOIDs = 0;
  4130. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4131. BAIL_IF_ERROR(hr);
  4132. if ( dwToken == TOKEN_IDENTIFIER )
  4133. {
  4134. int nIndex = FindSearchTableIndex( szToken,
  4135. aPropSearchTable,
  4136. dwSearchTableCount );
  4137. if ( nIndex != -1 )
  4138. {
  4139. nCount = 2;
  4140. *pOIDs = (int *) AllocADsMem( sizeof(int) * nCount);
  4141. if ( *pOIDs == NULL )
  4142. {
  4143. hr = E_OUTOFMEMORY;
  4144. BAIL_IF_ERROR(hr);
  4145. }
  4146. (*pOIDs)[nCurrent] = nIndex;
  4147. (*pOIDs)[++nCurrent] = -1;
  4148. }
  4149. }
  4150. else if ( dwToken == TOKEN_OPENBRACKET )
  4151. {
  4152. nCount = 10;
  4153. *pOIDs = (int *) AllocADsMem( sizeof(int) * nCount);
  4154. if ( *pOIDs == NULL )
  4155. {
  4156. hr = E_OUTOFMEMORY;
  4157. BAIL_IF_ERROR(hr);
  4158. }
  4159. do {
  4160. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4161. BAIL_IF_ERROR(hr);
  4162. if ( dwToken == TOKEN_CLOSEBRACKET )
  4163. {
  4164. FreeADsMem( *pOIDs );
  4165. *pOIDs = NULL;
  4166. goto cleanup;
  4167. }
  4168. if ( dwToken == TOKEN_IDENTIFIER )
  4169. {
  4170. int nIndex = FindSearchTableIndex( szToken,
  4171. aPropSearchTable,
  4172. dwSearchTableCount );
  4173. if ( nIndex != -1 )
  4174. {
  4175. if ( nCurrent == nCount )
  4176. {
  4177. pTemp = (int *) ReallocADsMem( *pOIDs,
  4178. sizeof(int) * nCount,
  4179. sizeof(int) * nCount * 2);
  4180. if ( pTemp == NULL )
  4181. {
  4182. hr = E_OUTOFMEMORY;
  4183. BAIL_IF_ERROR(hr);
  4184. }
  4185. *pOIDs = pTemp;
  4186. pTemp = NULL;
  4187. nCount *= 2;
  4188. }
  4189. (*pOIDs)[nCurrent++] = nIndex;
  4190. }
  4191. // else we cannot find the property, so skip over it.
  4192. }
  4193. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4194. BAIL_IF_ERROR(hr);
  4195. } while ( dwToken == TOKEN_DOLLAR );
  4196. if ( dwToken != TOKEN_CLOSEBRACKET )
  4197. {
  4198. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  4199. BAIL_IF_ERROR(hr);
  4200. }
  4201. if ( nCurrent == nCount )
  4202. {
  4203. // Need one extra NULL entry at the end of the array
  4204. pTemp = (int *) ReallocADsMem( *pOIDs,
  4205. sizeof(int) * nCount,
  4206. sizeof(int) * (nCount + 1));
  4207. if ( pTemp == NULL )
  4208. {
  4209. hr = E_OUTOFMEMORY;
  4210. BAIL_IF_ERROR(hr);
  4211. }
  4212. *pOIDs = pTemp;
  4213. pTemp = NULL;
  4214. nCount += 1;
  4215. }
  4216. (*pOIDs)[nCurrent] = -1;
  4217. }
  4218. else
  4219. {
  4220. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  4221. BAIL_IF_ERROR(hr);
  4222. }
  4223. if ( pnNumOfOIDs )
  4224. *pnNumOfOIDs = nCurrent;
  4225. cleanup:
  4226. if ( FAILED(hr))
  4227. {
  4228. if ( *pOIDs )
  4229. {
  4230. FreeADsMem( *pOIDs );
  4231. *pOIDs = NULL;
  4232. }
  4233. }
  4234. RRETURN(hr);
  4235. }
  4236. HRESULT
  4237. DirectoryString(CSchemaLexer * pTokenizer, LPTSTR *ppszDirString )
  4238. {
  4239. TCHAR szToken[MAX_TOKEN_LENGTH];
  4240. DWORD dwToken;
  4241. HRESULT hr;
  4242. if ( ppszDirString )
  4243. *ppszDirString = NULL;
  4244. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4245. BAIL_IF_ERROR(hr);
  4246. if ( dwToken == TOKEN_QUOTE )
  4247. {
  4248. hr = pTokenizer->GetNextToken2(szToken, &dwToken);
  4249. BAIL_IF_ERROR(hr);
  4250. if ( dwToken == TOKEN_IDENTIFIER )
  4251. {
  4252. if ( ppszDirString )
  4253. {
  4254. *ppszDirString = AllocADsStr( szToken );
  4255. if ( *ppszDirString == NULL )
  4256. {
  4257. hr = E_OUTOFMEMORY;
  4258. BAIL_IF_ERROR(hr);
  4259. }
  4260. }
  4261. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4262. BAIL_IF_ERROR(hr);
  4263. if ( dwToken == TOKEN_QUOTE )
  4264. RRETURN(S_OK);
  4265. }
  4266. }
  4267. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  4268. cleanup:
  4269. if ( FAILED(hr))
  4270. {
  4271. if ( ppszDirString && *ppszDirString )
  4272. {
  4273. FreeADsStr( *ppszDirString );
  4274. *ppszDirString = NULL;
  4275. }
  4276. }
  4277. RRETURN(hr);
  4278. }
  4279. //+---------------------------------------------------------------------------
  4280. // Function: DirectoryStrings
  4281. //
  4282. // Synopsis: This function is used to process ldap schema elements
  4283. // of the form qdstrings. This is defined in the RFC in detail :
  4284. //
  4285. // space = 1*" "
  4286. // whsp = [ space ]
  4287. // utf8 = <any sequence of octets formed from the UTF-8 [9]
  4288. // transformation of a character from ISO10646 [10]>
  4289. // dstring = 1*utf8
  4290. // qdstring = whsp "'" dstring "'" whsp
  4291. // qdstringlist = [ qdstring *( qdstring ) ]
  4292. // qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )
  4293. //
  4294. // Arguments: pTokenizer - The schema lexer object to use.
  4295. // pppszDirStrings - Return value for strings.
  4296. // pdwCount - Return value of number of strings.
  4297. //
  4298. //
  4299. // Returns: HRESULT - S_OK or any failure error code.
  4300. //
  4301. // Modifies: N/A.
  4302. //
  4303. // History: 7-12-2000 AjayR created.
  4304. //
  4305. //----------------------------------------------------------------------------
  4306. HRESULT
  4307. DirectoryStrings(
  4308. CSchemaLexer * pTokenizer,
  4309. LPTSTR **pppszDirStrings,
  4310. PDWORD pdwCount
  4311. )
  4312. {
  4313. HRESULT hr = S_OK;
  4314. TCHAR szToken[MAX_TOKEN_LENGTH];
  4315. LPWSTR *ppszTmp = NULL;
  4316. LPWSTR pszTmp = NULL;
  4317. DWORD dwToken, dwNumStrings = 0;
  4318. BOOL fNeedCloseBracket = FALSE;
  4319. ADsAssert(pTokenizer);
  4320. ADsAssert(pdwCount);
  4321. DWORD dwDummy = sizeof(ADS_ATTR_INFO);
  4322. *pdwCount = 0;
  4323. //
  4324. // Get the token type of the first token.
  4325. //
  4326. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4327. BAIL_ON_FAILURE(hr);
  4328. if (dwToken == TOKEN_OPENBRACKET) {
  4329. //
  4330. // In this case we know that there is more than one string.
  4331. // We can ignore the open bracket and continue to the next
  4332. // token which should be a quote.
  4333. //
  4334. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4335. BAIL_ON_FAILURE(hr);
  4336. fNeedCloseBracket = TRUE;
  4337. }
  4338. //
  4339. // Need to push what should be the quote in either case,
  4340. // back into the tokenizer (only then will the dirString
  4341. // routine work correctly.
  4342. //
  4343. hr = pTokenizer->PushBackToken();
  4344. BAIL_ON_FAILURE(hr);
  4345. if ( dwToken != TOKEN_QUOTE ) {
  4346. BAIL_ON_FAILURE(hr = E_FAIL);
  4347. }
  4348. //
  4349. // While there remain strings to be processed.
  4350. //
  4351. while (dwToken == TOKEN_QUOTE) {
  4352. hr = DirectoryString(
  4353. pTokenizer,
  4354. &pszTmp
  4355. );
  4356. BAIL_ON_FAILURE(hr);
  4357. if (dwNumStrings == 0) {
  4358. //
  4359. // Since we NULL terminate the array it should have
  4360. // at least 2 elements in this case.
  4361. //
  4362. ppszTmp = (LPWSTR *) AllocADsMem(sizeof(LPWSTR) * 2);
  4363. if (!ppszTmp) {
  4364. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4365. }
  4366. }
  4367. else {
  4368. LPWSTR *ppszLocal;
  4369. //
  4370. // To avoid passing the variable itself to local alloc.
  4371. //
  4372. ppszLocal = (LPWSTR *) ReallocADsMem(
  4373. ppszTmp,
  4374. sizeof(LPWSTR) * (dwNumStrings + 1),
  4375. sizeof(LPWSTR) * (dwNumStrings + 2)
  4376. );
  4377. if (ppszLocal) {
  4378. ppszTmp = ppszLocal;
  4379. }
  4380. else {
  4381. //
  4382. // Realloc failed, the old ptr is still valid.
  4383. //
  4384. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4385. }
  4386. }
  4387. ppszTmp[dwNumStrings] = pszTmp;
  4388. ppszTmp[++dwNumStrings] = NULL;
  4389. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4390. BAIL_ON_FAILURE(hr);
  4391. pTokenizer->PushBackToken();
  4392. } // end of while.
  4393. //
  4394. // If this was qdescrs and not just qdstring.
  4395. //
  4396. if (fNeedCloseBracket) {
  4397. hr = pTokenizer->GetNextToken(szToken, &dwToken);
  4398. BAIL_ON_FAILURE(hr);
  4399. if (dwToken != TOKEN_CLOSEBRACKET) {
  4400. //
  4401. // Not properly formed - should be just ignore ?
  4402. //
  4403. BAIL_ON_FAILURE(hr = E_FAIL);
  4404. }
  4405. }
  4406. //
  4407. // The count is the actual number not including the NULL string.
  4408. //
  4409. *pdwCount = dwNumStrings;
  4410. *pppszDirStrings = ppszTmp;
  4411. error:
  4412. if (FAILED(hr)) {
  4413. if (ppszTmp) {
  4414. //
  4415. // Free the strings if any and then the array itself.
  4416. //
  4417. for (DWORD dwCount = 0; dwCount < dwNumStrings; dwCount++) {
  4418. if (ppszTmp[dwCount]) {
  4419. FreeADsStr(ppszTmp[dwCount]);
  4420. }
  4421. }
  4422. FreeADsMem(ppszTmp);
  4423. ppszTmp = NULL;
  4424. }
  4425. //
  4426. // Need to reset the count.
  4427. //
  4428. *pdwCount = 0;
  4429. }
  4430. RRETURN(hr);
  4431. }
  4432. //+---------------------------------------------------------------------------
  4433. // Function: FreeDirectoryStrings
  4434. //
  4435. // Synopsis: This function is used to free the entries returned from
  4436. // the DirectoryStrings routine.
  4437. //
  4438. // Arguments: ppszDirStrings - List of strings to free.
  4439. // dwCount - Number of strings to free.
  4440. // fSkipFirstElement - If true, do not free the 1st element.
  4441. //
  4442. //
  4443. // Returns: N/A.
  4444. //
  4445. // Modifies: ppszDirStrings contents is freed including the array.
  4446. //
  4447. // History: 8-01-2000 AjayR created.
  4448. //
  4449. //----------------------------------------------------------------------------
  4450. void FreeDirectoryStrings(
  4451. LPTSTR *ppszDirStrings,
  4452. DWORD dwCount,
  4453. DWORD dwElementsToFree
  4454. )
  4455. {
  4456. DWORD dwCtr;
  4457. if (!ppszDirStrings) {
  4458. return;
  4459. }
  4460. switch (dwElementsToFree) {
  4461. case FREE_ALL_BUT_FIRST :
  4462. dwCtr = 1;
  4463. break;
  4464. case FREE_ALL :
  4465. dwCtr = 0;
  4466. break;
  4467. case FREE_ARRAY_NOT_ELEMENTS :
  4468. dwCtr = dwCount;
  4469. break;
  4470. }
  4471. for (; dwCtr < dwCount; dwCtr++) {
  4472. if (ppszDirStrings[dwCtr]) {
  4473. FreeADsStr(ppszDirStrings[dwCtr]);
  4474. ppszDirStrings[dwCtr] = NULL;
  4475. }
  4476. }
  4477. FreeADsMem(ppszDirStrings);
  4478. return;
  4479. }
  4480. //+---------------------------------------------------------------------------
  4481. // Function: AddNewNamesToPropertyArray --- Helper function.
  4482. //
  4483. // Synopsis: This function adds new entries to the property info array.
  4484. // Specifically, this fn is called when there are multiple names
  4485. // associated with the description of a single property. The new
  4486. // entries will have the same information as the current element
  4487. // but the appropriate new name.
  4488. //
  4489. // Arguments: ppPropArray - Property array containing current
  4490. // elements. This array is updated to contain
  4491. // the new elements on success and is
  4492. // untouched otherwise.
  4493. // dwCurPos - The current element being processed.
  4494. // dwCount - The current count of elements.
  4495. // ppszNewNames - Array of names to add (1st element is
  4496. // already a part of the property array).
  4497. //
  4498. // Returns: S_OK or E_OUTOFMEMORY.
  4499. //
  4500. // Modifies: *ppPropArray is modified in all success cases and some failure
  4501. // cases (realloc succeeds but not the subsequent string allocs).
  4502. //
  4503. // History: 10-03-2000 AjayR created.
  4504. //
  4505. //----------------------------------------------------------------------------
  4506. HRESULT
  4507. AddNewNamesToPropertyArray(
  4508. PROPERTYINFO **ppPropArray,
  4509. DWORD dwCurPos,
  4510. DWORD dwCount,
  4511. LPWSTR *ppszNewNames,
  4512. DWORD dwNewNameCount
  4513. )
  4514. {
  4515. HRESULT hr = S_OK;
  4516. PPROPERTYINFO pNewPropArray = NULL;
  4517. DWORD dwAdditions = 0;
  4518. DWORD dwCurCount = dwCount;
  4519. //
  4520. // The first element is already in the array.
  4521. //
  4522. dwAdditions = --dwNewNameCount;
  4523. if (!dwNewNameCount) {
  4524. RRETURN(hr);
  4525. }
  4526. //
  4527. // We need to realloc the new array and copy over the new elements.
  4528. //
  4529. pNewPropArray = (PROPERTYINFO *)
  4530. ReallocADsMem(
  4531. *ppPropArray,
  4532. (dwCurCount) * sizeof(PROPERTYINFO),
  4533. (dwCurCount + dwAdditions) * sizeof(PROPERTYINFO)
  4534. );
  4535. if (!pNewPropArray) {
  4536. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4537. }
  4538. //
  4539. // If the alloc succeeded we must return the new array.
  4540. //
  4541. *ppPropArray = pNewPropArray;
  4542. for (DWORD dwCtr = 0; dwCtr < dwAdditions; dwCtr++ ) {
  4543. PROPERTYINFO propOriginal = pNewPropArray[dwCurPos];
  4544. PROPERTYINFO *pPropNew = pNewPropArray + (dwCurPos + dwCtr + 1);
  4545. //
  4546. // Copy over the property. First all data that is not ptrs.
  4547. //
  4548. pPropNew->lMaxRange = propOriginal.lMaxRange;
  4549. pPropNew->lMinRange = propOriginal.lMinRange;
  4550. pPropNew->fSingleValued = propOriginal.fSingleValued;
  4551. pPropNew->fObsolete = propOriginal.fObsolete;
  4552. pPropNew->fCollective = propOriginal.fCollective;
  4553. pPropNew->fDynamic = propOriginal.fDynamic;
  4554. pPropNew->fNoUserModification = propOriginal.fNoUserModification;
  4555. pPropNew->dwUsage = propOriginal.dwUsage;
  4556. pPropNew->fProcessedSuperiorClass = propOriginal.fProcessedSuperiorClass;
  4557. //
  4558. // Now the strings.
  4559. //
  4560. pPropNew->pszOID = AllocADsStr(propOriginal.pszOID);
  4561. if (propOriginal.pszOID && !pPropNew->pszOID) {
  4562. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4563. }
  4564. pPropNew->pszSyntax = AllocADsStr(propOriginal.pszSyntax);
  4565. if (propOriginal.pszSyntax && !pPropNew->pszSyntax) {
  4566. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4567. }
  4568. pPropNew->pszDescription = AllocADsStr(propOriginal.pszDescription);
  4569. if (propOriginal.pszDescription && !pPropNew->pszDescription) {
  4570. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4571. }
  4572. pPropNew->pszOIDSup = AllocADsStr(propOriginal.pszOIDSup);
  4573. if (propOriginal.pszOIDSup && !pPropNew->pszOIDSup) {
  4574. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4575. }
  4576. pPropNew->pszOIDEquality = AllocADsStr(propOriginal.pszOIDEquality);
  4577. if (propOriginal.pszOIDEquality && !pPropNew->pszOIDEquality) {
  4578. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4579. }
  4580. pPropNew->pszOIDOrdering = AllocADsStr(propOriginal.pszOIDOrdering);
  4581. if (propOriginal.pszOIDOrdering && !pPropNew->pszOIDOrdering) {
  4582. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4583. }
  4584. pPropNew->pszOIDSubstr = AllocADsStr(propOriginal.pszOIDSubstr);
  4585. if (propOriginal.pszOIDSubstr && !pPropNew->pszOIDSubstr) {
  4586. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4587. }
  4588. //
  4589. // This is just a copy from the array.
  4590. //
  4591. pPropNew->pszPropertyName = ppszNewNames[dwCtr + 1];
  4592. }
  4593. //
  4594. // Success case.
  4595. //
  4596. RRETURN(hr);
  4597. error:
  4598. //
  4599. // Something failed, try and cleanup some pieces
  4600. //
  4601. if (pNewPropArray && (dwCtr != (DWORD)-1) ) {
  4602. //
  4603. // Cleanup the new elements added.
  4604. //
  4605. for (DWORD i = 0; i <= dwCtr; i++) {
  4606. PROPERTYINFO *pPropFree = pNewPropArray + (dwCurPos + i + 1);
  4607. //
  4608. // Free all the strings in this element except name.
  4609. //
  4610. if (pPropFree->pszOID) {
  4611. FreeADsStr(pPropFree->pszOID);
  4612. pPropFree->pszOID = NULL;
  4613. }
  4614. if (pPropFree->pszSyntax) {
  4615. FreeADsStr(pPropFree->pszSyntax);
  4616. pPropFree->pszSyntax = NULL;
  4617. }
  4618. if (pPropFree->pszDescription) {
  4619. FreeADsStr(pPropFree->pszDescription);
  4620. pPropFree->pszDescription = NULL;
  4621. }
  4622. if (pPropFree->pszOIDSup) {
  4623. FreeADsStr(pPropFree->pszOIDSup);
  4624. pPropFree->pszOIDSup = NULL;
  4625. }
  4626. if (pPropFree->pszOIDEquality) {
  4627. FreeADsStr(pPropFree->pszOIDEquality);
  4628. pPropFree->pszOIDEquality = NULL;
  4629. }
  4630. if (pPropFree->pszOIDOrdering) {
  4631. FreeADsStr(pPropFree->pszOIDOrdering);
  4632. pPropFree->pszOIDOrdering = NULL;
  4633. }
  4634. if (pPropFree->pszOIDSubstr) {
  4635. FreeADsStr(pPropFree->pszOIDSubstr);
  4636. pPropFree->pszOIDSubstr = NULL;
  4637. }
  4638. } // for
  4639. } // if we have elements to free
  4640. RRETURN(hr);
  4641. }
  4642. //+---------------------------------------------------------------------------
  4643. // Function: AddNewNamesToClassArray --- Helper function.
  4644. //
  4645. // Synopsis: This function adds new entries to the class info array.
  4646. // Specifically, this fn is called when there are multiple names
  4647. // associated with the description of a single class. The new
  4648. // entries will have the same information as the current element
  4649. // but the appropriate new name.
  4650. //
  4651. // Arguments: ppClassArray - Class array containing current
  4652. // elements. This array is updated to contain
  4653. // the new elements on success and is
  4654. // untouched otherwise.
  4655. // dwCurPos - The current element being processed.
  4656. // dwCount - The current count of elements.
  4657. // ppszNewNames - Array of names to add (1st element is
  4658. // already a part of the property array).
  4659. // dwNewNameCount - Number of elements in the new array.
  4660. //
  4661. // Returns: N/A.
  4662. //
  4663. // Modifies: *ppClassArray always on success and in some failure cases.
  4664. //
  4665. // History: 10-06-2000 AjayR created.
  4666. //
  4667. //----------------------------------------------------------------------------
  4668. HRESULT
  4669. AddNewNamesToClassArray(
  4670. CLASSINFO **ppClassArray,
  4671. DWORD dwCurPos,
  4672. DWORD dwCount,
  4673. LPWSTR *ppszNewNames,
  4674. DWORD dwNewNameCount
  4675. )
  4676. {
  4677. HRESULT hr = S_OK;
  4678. PCLASSINFO pNewClassArray = NULL;
  4679. DWORD dwAdditions = 0;
  4680. DWORD dwCurCount = dwCount;
  4681. int nCount;
  4682. //
  4683. // The first element is already in the array.
  4684. //
  4685. dwAdditions = --dwNewNameCount;
  4686. if (!dwNewNameCount) {
  4687. RRETURN(hr);
  4688. }
  4689. //
  4690. // We need to realloc the new array and copy over the new elements.
  4691. //
  4692. pNewClassArray = (CLASSINFO *)
  4693. ReallocADsMem(
  4694. *ppClassArray,
  4695. (dwCurCount) * sizeof(CLASSINFO),
  4696. (dwCurCount + dwAdditions) * sizeof(CLASSINFO)
  4697. );
  4698. if (!pNewClassArray) {
  4699. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4700. }
  4701. //
  4702. // If the alloc succeeded we must return the new array.
  4703. //
  4704. *ppClassArray = pNewClassArray;
  4705. for (DWORD dwCtr = 0; dwCtr < dwAdditions; dwCtr++ ) {
  4706. CLASSINFO classOriginal = pNewClassArray[dwCurPos];
  4707. CLASSINFO *pClassNew = pNewClassArray + (dwCurPos + dwCtr + 1);
  4708. //
  4709. // Copy over the property. First all data that is not ptrs.
  4710. //
  4711. pClassNew->dwType = classOriginal.dwType;
  4712. pClassNew->lHelpFileContext = classOriginal.lHelpFileContext;
  4713. pClassNew->fObsolete = classOriginal.fObsolete;
  4714. pClassNew->fProcessedSuperiorClasses =
  4715. classOriginal.fProcessedSuperiorClasses;
  4716. pClassNew->IsContainer = classOriginal.IsContainer;
  4717. //
  4718. // Now the strings and other pointers.
  4719. //
  4720. pClassNew->pszOID = AllocADsStr(classOriginal.pszOID);
  4721. if (classOriginal.pszOID && !pClassNew->pszOID) {
  4722. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4723. }
  4724. //
  4725. // The GUIDs are not copied over as they are not used or freed.
  4726. //
  4727. pClassNew->pszHelpFileName = AllocADsStr(classOriginal.pszHelpFileName);
  4728. if (classOriginal.pszHelpFileName && !pClassNew->pszHelpFileName) {
  4729. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4730. }
  4731. pClassNew->pszDescription = AllocADsStr(classOriginal.pszDescription);
  4732. if (classOriginal.pszDescription && !pClassNew->pszDescription) {
  4733. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4734. }
  4735. //
  4736. // pOIDsSuperiorClasses and pOIDsAuxClasses are arrays.
  4737. //
  4738. if (classOriginal.pOIDsSuperiorClasses) {
  4739. pClassNew->pOIDsSuperiorClasses =
  4740. CopyStringArray(classOriginal.pOIDsSuperiorClasses);
  4741. if (!pClassNew->pOIDsSuperiorClasses) {
  4742. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4743. }
  4744. }
  4745. if (classOriginal.pOIDsAuxClasses) {
  4746. pClassNew->pOIDsAuxClasses =
  4747. CopyStringArray(classOriginal.pOIDsAuxClasses);
  4748. if (!pClassNew->pOIDsAuxClasses) {
  4749. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4750. }
  4751. }
  4752. //
  4753. // Now the int arrays. Note that all of these will tag on the
  4754. // the last element (-1), -1 is not included in the count.
  4755. //
  4756. if (classOriginal.pOIDsMustContain) {
  4757. nCount = classOriginal.nNumOfMustContain + 1;
  4758. pClassNew->pOIDsMustContain =
  4759. (int *) AllocADsMem(sizeof(int) * nCount);
  4760. if (!pClassNew->pOIDsMustContain) {
  4761. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4762. }
  4763. memcpy(
  4764. pClassNew->pOIDsMustContain,
  4765. classOriginal.pOIDsMustContain,
  4766. sizeof(int) * nCount
  4767. );
  4768. pClassNew->nNumOfMustContain = --nCount;
  4769. }
  4770. if (classOriginal.pOIDsMayContain) {
  4771. nCount = classOriginal.nNumOfMayContain + 1;
  4772. pClassNew->pOIDsMayContain =
  4773. (int *) AllocADsMem(sizeof(int) * nCount);
  4774. if (!pClassNew->pOIDsMayContain) {
  4775. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4776. }
  4777. memcpy(
  4778. pClassNew->pOIDsMayContain,
  4779. classOriginal.pOIDsMayContain,
  4780. sizeof(int) * nCount
  4781. );
  4782. pClassNew->nNumOfMayContain = --nCount;
  4783. }
  4784. if (classOriginal.pOIDsNotContain) {
  4785. nCount = classOriginal.nNumOfNotContain + 1;
  4786. pClassNew->pOIDsNotContain =
  4787. (int *) AllocADsMem(sizeof(int) * nCount);
  4788. if (!pClassNew->pOIDsNotContain) {
  4789. BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
  4790. }
  4791. memcpy(
  4792. pClassNew->pOIDsNotContain,
  4793. classOriginal.pOIDsNotContain,
  4794. sizeof(int) * nCount
  4795. );
  4796. pClassNew->nNumOfNotContain = --nCount;
  4797. }
  4798. //
  4799. // This is just a copy from the array.
  4800. //
  4801. pClassNew->pszName = ppszNewNames[dwCtr + 1];
  4802. }
  4803. //
  4804. // Success case.
  4805. //
  4806. RRETURN(hr);
  4807. error:
  4808. //
  4809. // Something failed, try and cleanup some pieces
  4810. //
  4811. if (pNewClassArray && (dwCtr != (DWORD)-1) ) {
  4812. //
  4813. // Cleanup the new elements added.
  4814. //
  4815. for (DWORD i = 0; i <= dwCtr; i++) {
  4816. CLASSINFO *pClassFree = pNewClassArray + (dwCurPos + i + 1);
  4817. //
  4818. // Free all the strings in this element except name.
  4819. //
  4820. if (pClassFree->pszOID) {
  4821. FreeADsStr(pClassFree->pszOID);
  4822. pClassFree->pszOID = NULL;
  4823. }
  4824. if (pClassFree->pszHelpFileName) {
  4825. FreeADsStr(pClassFree->pszHelpFileName);
  4826. pClassFree->pszHelpFileName = NULL;
  4827. }
  4828. if (pClassFree->pszDescription) {
  4829. FreeADsStr(pClassFree->pszDescription);
  4830. pClassFree->pszDescription = NULL;
  4831. }
  4832. //
  4833. // Now the string arrays.
  4834. //
  4835. if (pClassFree->pOIDsSuperiorClasses) {
  4836. nCount = 0;
  4837. LPTSTR pszTemp;
  4838. while (pszTemp = (pClassFree->pOIDsSuperiorClasses)[nCount]) {
  4839. FreeADsStr(pszTemp);
  4840. nCount++;
  4841. }
  4842. FreeADsMem(pClassFree->pOIDsSuperiorClasses);
  4843. pClassFree->pOIDsSuperiorClasses = NULL;
  4844. }
  4845. if (pClassFree->pOIDsAuxClasses) {
  4846. nCount = 0;
  4847. LPTSTR pszTemp;
  4848. while (pszTemp = (pClassFree->pOIDsAuxClasses)[nCount]) {
  4849. FreeADsStr(pszTemp);
  4850. nCount++;
  4851. }
  4852. FreeADsMem(pClassFree->pOIDsAuxClasses);
  4853. pClassFree->pOIDsAuxClasses = NULL;
  4854. }
  4855. if (pClassFree->pOIDsMustContain) {
  4856. FreeADsMem(pClassFree->pOIDsMustContain);
  4857. pClassFree->pOIDsMustContain = NULL;
  4858. pClassFree->nNumOfMustContain = 0;
  4859. }
  4860. if (pClassFree->pOIDsMayContain) {
  4861. FreeADsMem(pClassFree->pOIDsMayContain);
  4862. pClassFree->pOIDsMayContain = NULL;
  4863. pClassFree->nNumOfMayContain = 0;
  4864. }
  4865. if (pClassFree->pOIDsNotContain) {
  4866. FreeADsMem(pClassFree->pOIDsNotContain);
  4867. pClassFree->pOIDsNotContain = NULL;
  4868. pClassFree->nNumOfNotContain = 0;
  4869. }
  4870. } // for
  4871. } // if we have elements to free
  4872. RRETURN(hr);
  4873. }
  4874. //+---------------------------------------------------------------------------
  4875. // Function:
  4876. //
  4877. // Synopsis:
  4878. //
  4879. // Arguments:
  4880. //
  4881. // Returns:
  4882. //
  4883. // Modifies:
  4884. //
  4885. // History: 11-3-95 krishnag Created.
  4886. //
  4887. //----------------------------------------------------------------------------
  4888. CSchemaLexer::CSchemaLexer():
  4889. _ptr(NULL),
  4890. _Buffer(NULL),
  4891. _dwLastTokenLength(0),
  4892. _dwLastToken(0),
  4893. _dwEndofString(0),
  4894. _fInQuotes(FALSE)
  4895. {}
  4896. //+---------------------------------------------------------------------------
  4897. // Function:
  4898. //
  4899. // Synopsis:
  4900. //
  4901. // Arguments:
  4902. //
  4903. // Returns:
  4904. //
  4905. // Modifies:
  4906. //
  4907. // History: 08-12-96 t-danal Created.
  4908. //
  4909. //----------------------------------------------------------------------------
  4910. CSchemaLexer::~CSchemaLexer()
  4911. {
  4912. FreeADsStr(_Buffer);
  4913. }
  4914. //+---------------------------------------------------------------------------
  4915. // Function:
  4916. //
  4917. // Synopsis:
  4918. //
  4919. // Arguments:
  4920. //
  4921. // Returns:
  4922. //
  4923. // Modifies:
  4924. //
  4925. // History: 11-3-95 krishnag Created.
  4926. //
  4927. //----------------------------------------------------------------------------
  4928. HRESULT
  4929. CSchemaLexer::GetNextToken(LPTSTR szToken, LPDWORD pdwToken)
  4930. {
  4931. TCHAR c;
  4932. DWORD state = 0;
  4933. LPTSTR pch = szToken;
  4934. memset(szToken, 0, sizeof(TCHAR) * MAX_TOKEN_LENGTH);
  4935. _dwLastTokenLength = 0;
  4936. while (1) {
  4937. c = NextChar();
  4938. switch (state) {
  4939. case 0:
  4940. *pch++ = c;
  4941. _dwLastTokenLength++;
  4942. switch (c) {
  4943. case TEXT('(') :
  4944. *pdwToken = TOKEN_OPENBRACKET;
  4945. _dwLastToken = *pdwToken;
  4946. RRETURN(S_OK);
  4947. break;
  4948. case TEXT(')') :
  4949. *pdwToken = TOKEN_CLOSEBRACKET;
  4950. _dwLastToken = *pdwToken;
  4951. RRETURN(S_OK);
  4952. break;
  4953. case TEXT('\'') :
  4954. *pdwToken = TOKEN_QUOTE;
  4955. _dwLastToken = *pdwToken;
  4956. _fInQuotes = !_fInQuotes;
  4957. RRETURN(S_OK);
  4958. break;
  4959. case TEXT('$') :
  4960. *pdwToken = TOKEN_DOLLAR;
  4961. _dwLastToken = *pdwToken;
  4962. RRETURN(S_OK);
  4963. break;
  4964. case TEXT(' ') :
  4965. pch--;
  4966. _dwLastTokenLength--;
  4967. break;
  4968. case TEXT('\0') :
  4969. *pdwToken = TOKEN_END;
  4970. _dwLastToken = *pdwToken;
  4971. RRETURN(S_OK);
  4972. break;
  4973. case TEXT('{') :
  4974. *pdwToken = TOKEN_OPEN_CURLY;
  4975. _dwLastToken = *pdwToken;
  4976. RRETURN(S_OK);
  4977. break;
  4978. case TEXT('}') :
  4979. *pdwToken = TOKEN_CLOSE_CURLY;
  4980. _dwLastToken = *pdwToken;
  4981. RRETURN(S_OK);
  4982. break;
  4983. default:
  4984. state = 1;
  4985. break;
  4986. } // end of switch c
  4987. break;
  4988. case 1:
  4989. switch (c) {
  4990. case TEXT('(') :
  4991. case TEXT(')') :
  4992. case TEXT('\'') :
  4993. case TEXT('$') :
  4994. case TEXT(' ') :
  4995. case TEXT('\0') :
  4996. case TEXT('{') :
  4997. case TEXT('}') :
  4998. if ( _fInQuotes && c != TEXT('\''))
  4999. {
  5000. if ( c == TEXT('\0'))
  5001. RRETURN(E_FAIL);
  5002. *pch++ = c;
  5003. _dwLastTokenLength++;
  5004. state = 1;
  5005. break;
  5006. }
  5007. else // Not in quotes or in quotes and reach the matching quote
  5008. {
  5009. PushbackChar();
  5010. *pdwToken = TOKEN_IDENTIFIER;
  5011. _dwLastToken = *pdwToken;
  5012. RRETURN (S_OK);
  5013. }
  5014. break;
  5015. default :
  5016. *pch++ = c;
  5017. _dwLastTokenLength++;
  5018. state = 1;
  5019. break;
  5020. } // switch c
  5021. break;
  5022. default:
  5023. RRETURN(E_FAIL);
  5024. } // switch state
  5025. }
  5026. }
  5027. HRESULT
  5028. CSchemaLexer::GetNextToken2(LPTSTR szToken, LPDWORD pdwToken)
  5029. {
  5030. TCHAR c;
  5031. DWORD state = 0;
  5032. LPTSTR pch = szToken;
  5033. memset(szToken, 0, sizeof(TCHAR) * MAX_TOKEN_LENGTH);
  5034. _dwLastTokenLength = 0;
  5035. while (1) {
  5036. c = NextChar();
  5037. switch (state) {
  5038. case 0:
  5039. *pch++ = c;
  5040. _dwLastTokenLength++;
  5041. switch (c) {
  5042. case TEXT('(') :
  5043. *pdwToken = TOKEN_OPENBRACKET;
  5044. _dwLastToken = *pdwToken;
  5045. RRETURN(S_OK);
  5046. break;
  5047. case TEXT(')') :
  5048. *pdwToken = TOKEN_CLOSEBRACKET;
  5049. _dwLastToken = *pdwToken;
  5050. RRETURN(S_OK);
  5051. break;
  5052. case TEXT('\'') :
  5053. *pdwToken = TOKEN_QUOTE;
  5054. _dwLastToken = *pdwToken;
  5055. _fInQuotes = !_fInQuotes;
  5056. RRETURN(S_OK);
  5057. break;
  5058. case TEXT('$') :
  5059. *pdwToken = TOKEN_DOLLAR;
  5060. _dwLastToken = *pdwToken;
  5061. RRETURN(S_OK);
  5062. break;
  5063. case TEXT('\0') :
  5064. *pdwToken = TOKEN_END;
  5065. _dwLastToken = *pdwToken;
  5066. RRETURN(S_OK);
  5067. break;
  5068. case TEXT('{') :
  5069. *pdwToken = TOKEN_OPEN_CURLY;
  5070. _dwLastToken = *pdwToken;
  5071. RRETURN(S_OK);
  5072. break;
  5073. case TEXT('}') :
  5074. *pdwToken = TOKEN_CLOSE_CURLY;
  5075. _dwLastToken = *pdwToken;
  5076. RRETURN(S_OK);
  5077. break;
  5078. default:
  5079. state = 1;
  5080. break;
  5081. } // end of switch c
  5082. break;
  5083. case 1:
  5084. switch (c) {
  5085. case TEXT('(') :
  5086. case TEXT(')') :
  5087. case TEXT('\'') :
  5088. case TEXT('$') :
  5089. case TEXT(' ') :
  5090. case TEXT('\0') :
  5091. case TEXT('{') :
  5092. case TEXT('}') :
  5093. if ( _fInQuotes && c != TEXT('\''))
  5094. {
  5095. if ( c == TEXT('\0'))
  5096. RRETURN(E_FAIL);
  5097. *pch++ = c;
  5098. _dwLastTokenLength++;
  5099. state = 1;
  5100. break;
  5101. }
  5102. else // Not in quotes or in quotes and reach the matching quote
  5103. {
  5104. PushbackChar();
  5105. *pdwToken = TOKEN_IDENTIFIER;
  5106. _dwLastToken = *pdwToken;
  5107. RRETURN (S_OK);
  5108. }
  5109. break;
  5110. default :
  5111. *pch++ = c;
  5112. _dwLastTokenLength++;
  5113. state = 1;
  5114. break;
  5115. } // switch c
  5116. break;
  5117. default:
  5118. RRETURN(E_FAIL);
  5119. } // switch state
  5120. }
  5121. }
  5122. //+---------------------------------------------------------------------------
  5123. // Function:
  5124. //
  5125. // Synopsis:
  5126. //
  5127. // Arguments:
  5128. //
  5129. // Returns:
  5130. //
  5131. // Modifies:
  5132. //
  5133. // History: 11-3-95 krishnag Created.
  5134. //
  5135. //----------------------------------------------------------------------------
  5136. TCHAR
  5137. CSchemaLexer::NextChar()
  5138. {
  5139. if (_ptr == NULL || *_ptr == TEXT('\0')) {
  5140. _dwEndofString = TRUE;
  5141. return(TEXT('\0'));
  5142. }
  5143. return(*_ptr++);
  5144. }
  5145. //+---------------------------------------------------------------------------
  5146. // Function:
  5147. //
  5148. // Synopsis: ONLY ONE TOKEN CAN BE PUSHED BACK.
  5149. //
  5150. // Arguments:
  5151. //
  5152. // Returns:
  5153. //
  5154. // Modifies:
  5155. //
  5156. // History: 11-3-95 krishnag Created.
  5157. //
  5158. //----------------------------------------------------------------------------
  5159. HRESULT
  5160. CSchemaLexer::PushBackToken()
  5161. {
  5162. DWORD i = 0;
  5163. if (_dwLastToken == TOKEN_END) {
  5164. RRETURN(S_OK);
  5165. }
  5166. for (i=0; i < _dwLastTokenLength; i++) {
  5167. if (*(--_ptr) == TEXT('\'') ) {
  5168. _fInQuotes = !_fInQuotes;
  5169. }
  5170. }
  5171. RRETURN(S_OK);
  5172. }
  5173. //+---------------------------------------------------------------------------
  5174. // Function:
  5175. //
  5176. // Synopsis:
  5177. //
  5178. // Arguments:
  5179. //
  5180. // Returns:
  5181. //
  5182. // Modifies:
  5183. //
  5184. // History: 11-3-95 krishnag Created.
  5185. //
  5186. //----------------------------------------------------------------------------
  5187. void
  5188. CSchemaLexer::PushbackChar()
  5189. {
  5190. if (_dwEndofString) {
  5191. return;
  5192. }
  5193. _ptr--;
  5194. }
  5195. //+---------------------------------------------------------------------------
  5196. // Function:
  5197. //
  5198. // Synopsis:
  5199. //
  5200. // Arguments:
  5201. //
  5202. // Returns:
  5203. //
  5204. // Modifies:
  5205. //
  5206. // History: 11-3-95 krishnag Created.
  5207. //
  5208. //----------------------------------------------------------------------------
  5209. BOOL
  5210. CSchemaLexer::IsKeyword(LPTSTR szToken, LPDWORD pdwToken)
  5211. {
  5212. DWORD i = 0;
  5213. for (i = 0; i < g_dwSchemaKeywordListSize; i++) {
  5214. if (!_tcsicmp(szToken, g_aSchemaKeywordList[i].Keyword)) {
  5215. *pdwToken = g_aSchemaKeywordList[i].dwTokenId;
  5216. return(TRUE);
  5217. }
  5218. else if (!_wcsnicmp(szToken, L"X-", 2)) {
  5219. //
  5220. // Terms begining with X- are special tokens for schema.
  5221. //
  5222. *pdwToken = TOKEN_X;
  5223. return(TRUE);
  5224. }
  5225. }
  5226. *pdwToken = 0;
  5227. return(FALSE);
  5228. }
  5229. HRESULT
  5230. CSchemaLexer::InitializePath(LPTSTR szBuffer)
  5231. {
  5232. HRESULT hr = S_OK;
  5233. _Buffer = AllocADsStr(szBuffer);
  5234. if(!_Buffer && szBuffer)
  5235. {
  5236. hr = E_OUTOFMEMORY;
  5237. }
  5238. _ptr = _Buffer;
  5239. return hr;
  5240. }
  5241. int _cdecl searchentrycmp( const void *s1, const void *s2 )
  5242. {
  5243. SEARCHENTRY *srch1 = (SEARCHENTRY *) s1;
  5244. SEARCHENTRY *srch2 = (SEARCHENTRY *) s2;
  5245. return ( _tcsicmp( srch1->pszName, srch2->pszName ));
  5246. }
  5247. int _cdecl intcmp( const void *s1, const void *s2 )
  5248. {
  5249. int n1 = *((int *) s1);
  5250. int n2 = *((int *) s2);
  5251. int retval;
  5252. if ( n1 == n2 )
  5253. retval = 0;
  5254. else if ( n1 < n2 )
  5255. retval = -1;
  5256. else
  5257. retval = 1;
  5258. return retval;
  5259. }
  5260. long CompareUTCTime(
  5261. LPTSTR pszTime1,
  5262. LPTSTR pszTime2
  5263. )
  5264. {
  5265. SYSTEMTIME sysTime1;
  5266. SYSTEMTIME sysTime2;
  5267. FILETIME fTime1;
  5268. FILETIME fTime2;
  5269. memset( &sysTime1, 0, sizeof(sysTime1));
  5270. memset( &sysTime2, 0, sizeof(sysTime2));
  5271. //
  5272. // We are ignoring the last part which might be a float.
  5273. // The time window is sufficiently small for us not to
  5274. // worry about this value.
  5275. //
  5276. _stscanf( pszTime1, TEXT("%4d%2d%2d%2d%2d%2d"),
  5277. &sysTime1.wYear,
  5278. &sysTime1.wMonth,
  5279. &sysTime1.wDay,
  5280. &sysTime1.wHour,
  5281. &sysTime1.wMinute,
  5282. &sysTime1.wSecond
  5283. );
  5284. _stscanf( pszTime2, TEXT("%4d%2d%2d%2d%2d%2d"),
  5285. &sysTime2.wYear,
  5286. &sysTime2.wMonth,
  5287. &sysTime2.wDay,
  5288. &sysTime2.wHour,
  5289. &sysTime2.wMinute,
  5290. &sysTime2.wSecond
  5291. );
  5292. if ( SystemTimeToFileTime( &sysTime1, &fTime1 )
  5293. && SystemTimeToFileTime( &sysTime2, &fTime2 )
  5294. )
  5295. {
  5296. return CompareFileTime( &fTime1, &fTime2 );
  5297. }
  5298. // If SystemTimeToFileTime failed, then assume that pszTime1 is in cache,
  5299. // pszTime2 is on the server and if we cannot get the correct time, we
  5300. // should always read from the server again. Hence, return -1;
  5301. return -1;
  5302. }
  5303. int FindEntryInSearchTable( LPTSTR pszName, SEARCHENTRY *aSearchTable, DWORD nSearchTableSize)
  5304. {
  5305. SEARCHENTRY searchEntry;
  5306. SEARCHENTRY *matchedEntry = NULL;
  5307. searchEntry.pszName = pszName;
  5308. matchedEntry = (SEARCHENTRY *) bsearch(
  5309. (SEARCHENTRY *) &searchEntry,
  5310. aSearchTable, nSearchTableSize,
  5311. sizeof(SEARCHENTRY), searchentrycmp );
  5312. if ( matchedEntry )
  5313. {
  5314. return matchedEntry->nIndex;
  5315. }
  5316. return -1;
  5317. }
  5318. int FindSearchTableIndex( LPTSTR pszName, SEARCHENTRY *aSearchTable, DWORD nSearchTableSize )
  5319. {
  5320. SEARCHENTRY searchEntry;
  5321. SEARCHENTRY *matchedEntry = NULL;
  5322. searchEntry.pszName = pszName;
  5323. matchedEntry = (SEARCHENTRY *) bsearch(
  5324. (SEARCHENTRY *) &searchEntry,
  5325. aSearchTable, nSearchTableSize,
  5326. sizeof(SEARCHENTRY), searchentrycmp );
  5327. if ( matchedEntry )
  5328. {
  5329. return (int)( matchedEntry - aSearchTable ); // return index of search table
  5330. }
  5331. return -1;
  5332. }
  5333. HRESULT
  5334. ReadSubSchemaSubEntry(
  5335. LPWSTR pszLDAPServer,
  5336. LPWSTR * ppszSubSchemaEntry,
  5337. OUT BOOL *pfBoundOk, // have we at least once bound to domain
  5338. // successfully, OPTIONAL (can be NULL)
  5339. CCredentials& Credentials,
  5340. DWORD dwPort
  5341. )
  5342. {
  5343. HRESULT hr = S_OK;
  5344. ROOTDSENODE rootDSE = {0};
  5345. ADsAssert(ppszSubSchemaEntry);
  5346. *ppszSubSchemaEntry = NULL;
  5347. //
  5348. // Call the generic function
  5349. //
  5350. hr = ReadRootDSENode(
  5351. pszLDAPServer,
  5352. &rootDSE,
  5353. pfBoundOk,
  5354. Credentials,
  5355. dwPort
  5356. );
  5357. BAIL_ON_FAILURE(hr);
  5358. if ( !rootDSE.pszSubSchemaEntry ) {
  5359. //
  5360. // SubschemaEntry must be found
  5361. //
  5362. BAIL_ON_FAILURE(hr = E_FAIL);
  5363. }
  5364. else {
  5365. *ppszSubSchemaEntry = rootDSE.pszSubSchemaEntry;
  5366. }
  5367. error:
  5368. RRETURN(hr);
  5369. }
  5370. HRESULT
  5371. ReadPagingSupportedAttr(
  5372. LPWSTR pszLDAPServer,
  5373. BOOL * pfPagingSupported,
  5374. CCredentials& Credentials,
  5375. DWORD dwPort
  5376. )
  5377. {
  5378. HRESULT hr = S_OK;
  5379. ROOTDSENODE rootDSE = {0};
  5380. ADsAssert(pfPagingSupported);
  5381. *pfPagingSupported = FALSE;
  5382. //
  5383. // Call the generic function
  5384. //
  5385. hr = ReadRootDSENode(
  5386. pszLDAPServer,
  5387. &rootDSE,
  5388. NULL,
  5389. Credentials,
  5390. dwPort
  5391. );
  5392. BAIL_ON_FAILURE(hr);
  5393. if ( rootDSE.pszSubSchemaEntry) {
  5394. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5395. }
  5396. *pfPagingSupported = rootDSE.fPagingSupported;
  5397. error:
  5398. RRETURN(hr);
  5399. }
  5400. HRESULT
  5401. ReadSortingSupportedAttr(
  5402. LPWSTR pszLDAPServer,
  5403. BOOL * pfSortingSupported,
  5404. CCredentials& Credentials,
  5405. DWORD dwPort
  5406. )
  5407. {
  5408. HRESULT hr = S_OK;
  5409. ROOTDSENODE rootDSE = {0};
  5410. ADsAssert(pfSortingSupported);
  5411. *pfSortingSupported = FALSE;
  5412. //
  5413. // Call the generic function
  5414. //
  5415. hr = ReadRootDSENode(
  5416. pszLDAPServer,
  5417. &rootDSE,
  5418. NULL,
  5419. Credentials,
  5420. dwPort
  5421. );
  5422. BAIL_ON_FAILURE(hr);
  5423. if ( rootDSE.pszSubSchemaEntry) {
  5424. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5425. }
  5426. *pfSortingSupported = rootDSE.fSortingSupported;
  5427. error:
  5428. RRETURN(hr);
  5429. }
  5430. HRESULT
  5431. ReadAttribScopedSupportedAttr(
  5432. LPWSTR pszLDAPServer,
  5433. BOOL * pfAttribScopedSupported,
  5434. CCredentials& Credentials,
  5435. DWORD dwPort
  5436. )
  5437. {
  5438. HRESULT hr = S_OK;
  5439. ROOTDSENODE rootDSE = {0};
  5440. ADsAssert(pfAttribScopedSupported);
  5441. *pfAttribScopedSupported = FALSE;
  5442. //
  5443. // Call the generic function
  5444. //
  5445. hr = ReadRootDSENode(
  5446. pszLDAPServer,
  5447. &rootDSE,
  5448. NULL,
  5449. Credentials,
  5450. dwPort
  5451. );
  5452. BAIL_ON_FAILURE(hr);
  5453. if ( rootDSE.pszSubSchemaEntry) {
  5454. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5455. }
  5456. *pfAttribScopedSupported = rootDSE.fAttribScopedSupported;
  5457. error:
  5458. RRETURN(hr);
  5459. }
  5460. HRESULT
  5461. ReadVLVSupportedAttr(
  5462. LPWSTR pszLDAPServer,
  5463. BOOL * pfVLVSupported,
  5464. CCredentials& Credentials,
  5465. DWORD dwPort
  5466. )
  5467. {
  5468. HRESULT hr = S_OK;
  5469. ROOTDSENODE rootDSE = {0};
  5470. ADsAssert(pfVLVSupported);
  5471. *pfVLVSupported = FALSE;
  5472. //
  5473. // Call the generic function
  5474. //
  5475. hr = ReadRootDSENode(
  5476. pszLDAPServer,
  5477. &rootDSE,
  5478. NULL,
  5479. Credentials,
  5480. dwPort
  5481. );
  5482. BAIL_ON_FAILURE(hr);
  5483. if ( rootDSE.pszSubSchemaEntry) {
  5484. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5485. }
  5486. *pfVLVSupported = rootDSE.fVLVSupported;
  5487. error:
  5488. RRETURN(hr);
  5489. }
  5490. //
  5491. // Returns the info about SecDesc Control if appropriate
  5492. //
  5493. HRESULT
  5494. ReadSecurityDescriptorControlType(
  5495. LPWSTR pszLDAPServer,
  5496. DWORD * pdwSecDescType,
  5497. CCredentials& Credentials,
  5498. DWORD dwPort
  5499. )
  5500. {
  5501. HRESULT hr = S_OK;
  5502. ROOTDSENODE rootDSE = {0};
  5503. ADsAssert(pdwSecDescType);
  5504. *pdwSecDescType = ADSI_LDAPC_SECDESC_NONE;
  5505. //
  5506. // Call the generic function
  5507. //
  5508. hr = ReadRootDSENode(
  5509. pszLDAPServer,
  5510. &rootDSE,
  5511. NULL,
  5512. Credentials,
  5513. dwPort
  5514. );
  5515. BAIL_ON_FAILURE(hr);
  5516. if ( rootDSE.pszSubSchemaEntry) {
  5517. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5518. }
  5519. *pdwSecDescType = rootDSE.dwSecDescType;
  5520. error:
  5521. //
  5522. // Since the error case is uninteresting, if there was an
  5523. // error, we will continue with no sec desc
  5524. //
  5525. if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE))
  5526. RRETURN (S_OK);
  5527. else
  5528. RRETURN(hr);
  5529. }
  5530. //
  5531. // This is to see if we support the domain scope control.
  5532. // If we do we can set it to reduce server load.
  5533. //
  5534. HRESULT
  5535. ReadDomScopeSupportedAttr(
  5536. LPWSTR pszLDAPServer,
  5537. BOOL * pfDomScopeSupported,
  5538. CCredentials& Credentials,
  5539. DWORD dwPort
  5540. )
  5541. {
  5542. HRESULT hr = S_OK;
  5543. ROOTDSENODE rootDSE = {0};
  5544. ADsAssert(pfDomScopeSupported);
  5545. *pfDomScopeSupported = FALSE;
  5546. //
  5547. // Call the generic function
  5548. //
  5549. hr = ReadRootDSENode(
  5550. pszLDAPServer,
  5551. &rootDSE,
  5552. NULL,
  5553. Credentials,
  5554. dwPort
  5555. );
  5556. BAIL_ON_FAILURE(hr);
  5557. if ( rootDSE.pszSubSchemaEntry) {
  5558. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5559. }
  5560. *pfDomScopeSupported = rootDSE.fDomScopeSupported;
  5561. error:
  5562. RRETURN(hr);
  5563. }
  5564. //
  5565. // This is to see if we support the domain scope control.
  5566. // If we do we can set it to reduce server load.
  5567. //
  5568. HRESULT
  5569. ReadServerSupportsIsADControl(
  5570. LPWSTR pszLDAPServer,
  5571. BOOL * pfServerIsAD,
  5572. CCredentials& Credentials,
  5573. DWORD dwPort
  5574. )
  5575. {
  5576. HRESULT hr = S_OK;
  5577. ROOTDSENODE rootDSE = {0};
  5578. ADsAssert(pfServerIsAD);
  5579. *pfServerIsAD = FALSE;
  5580. //
  5581. // Call the generic function
  5582. //
  5583. hr = ReadRootDSENode(
  5584. pszLDAPServer,
  5585. &rootDSE,
  5586. NULL,
  5587. Credentials,
  5588. dwPort
  5589. );
  5590. BAIL_ON_FAILURE(hr);
  5591. if ( rootDSE.pszSubSchemaEntry) {
  5592. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5593. }
  5594. *pfServerIsAD = rootDSE.fTalkingToAD;
  5595. error:
  5596. RRETURN(hr);
  5597. }
  5598. //
  5599. // This is to see if we are talking to enhacned AD servers so we
  5600. // can process the aux classes correctly.
  5601. //
  5602. HRESULT
  5603. ReadServerSupportsIsEnhancedAD(
  5604. LPWSTR pszLDAPServer,
  5605. BOOL * pfServerIsEnhancedAD,
  5606. BOOL * pfServerIsADControl,
  5607. CCredentials& Credentials,
  5608. DWORD dwPort
  5609. )
  5610. {
  5611. HRESULT hr = S_OK;
  5612. ROOTDSENODE rootDSE = {0};
  5613. ADsAssert(pfServerIsEnhancedAD);
  5614. ADsAssert(pfServerIsADControl);
  5615. *pfServerIsEnhancedAD = FALSE;
  5616. *pfServerIsADControl = FALSE;
  5617. //
  5618. // Call the generic function
  5619. //
  5620. hr = ReadRootDSENode(
  5621. pszLDAPServer,
  5622. &rootDSE,
  5623. NULL,
  5624. Credentials,
  5625. dwPort
  5626. );
  5627. BAIL_ON_FAILURE(hr);
  5628. if ( rootDSE.pszSubSchemaEntry) {
  5629. FreeADsStr (rootDSE.pszSubSchemaEntry);
  5630. }
  5631. *pfServerIsEnhancedAD = rootDSE.fTalkingToEnhancedAD;
  5632. *pfServerIsADControl = rootDSE.fTalkingToAD;
  5633. error:
  5634. RRETURN(hr);
  5635. }
  5636. BOOL
  5637. EquivalentServers(
  5638. LPWSTR pszTargetServer,
  5639. LPWSTR pszSourceServer
  5640. )
  5641. {
  5642. if (!pszTargetServer && !pszSourceServer) {
  5643. return(TRUE);
  5644. }
  5645. if (pszTargetServer && pszSourceServer) {
  5646. #ifdef WIN95
  5647. if (!_wcsicmp(pszTargetServer, pszSourceServer)) {
  5648. #else
  5649. if (CompareStringW(
  5650. LOCALE_SYSTEM_DEFAULT,
  5651. NORM_IGNORECASE,
  5652. pszTargetServer,
  5653. -1,
  5654. pszSourceServer,
  5655. -1
  5656. ) == CSTR_EQUAL ) {
  5657. #endif
  5658. return(TRUE);
  5659. }
  5660. }
  5661. return(FALSE);
  5662. }
  5663. BOOL
  5664. EquivalentUsers(
  5665. LPWSTR pszUser1,
  5666. LPWSTR pszUser2
  5667. )
  5668. {
  5669. if (!pszUser1 && !pszUser2) {
  5670. return(TRUE);
  5671. }
  5672. if (pszUser1 && pszUser2) {
  5673. #ifdef WIN95
  5674. if (!_wcsicmp(pszUser1, pszUser2)) {
  5675. #else
  5676. if (CompareStringW(
  5677. LOCALE_SYSTEM_DEFAULT,
  5678. NORM_IGNORECASE,
  5679. pszUser1,
  5680. -1,
  5681. pszUser2,
  5682. -1
  5683. ) == CSTR_EQUAL ) {
  5684. #endif
  5685. return(TRUE);
  5686. }
  5687. }
  5688. return(FALSE);
  5689. }
  5690. HRESULT
  5691. ReadRootDSENode(
  5692. LPWSTR pszLDAPServer,
  5693. PROOTDSENODE pRootDSE,
  5694. OUT BOOL * pfBoundOk, // have we at least once bound to domain
  5695. // successfully, OPTIONAL (can be NULL)
  5696. CCredentials& Credentials,
  5697. DWORD dwPort
  5698. )
  5699. {
  5700. HRESULT hr = S_OK;
  5701. PSCHEMALIST pTemp = NULL;
  5702. PSCHEMALIST pNewNode = NULL;
  5703. ADS_LDP * ld = NULL;
  5704. int nCount1 = 0, nCount2 = 0, nCount3 = 0;
  5705. LPWSTR *aValues1 = NULL, *aValues2 = NULL, *aValues3 = NULL;
  5706. LDAPMessage * res = NULL;
  5707. LDAPMessage *e = NULL;
  5708. LPWSTR aStrings[4]; // Attributes to fetch.
  5709. BOOL fBoundOk = FALSE; // have we at least once bound to
  5710. // domain successfully?
  5711. BOOL fNoData = FALSE;
  5712. ADsAssert(pRootDSE);
  5713. memset (pRootDSE, 0x00, sizeof(ROOTDSENODE));
  5714. ENTER_SUBSCHEMA_CRITSECT();
  5715. pTemp = gpSubSchemaList;
  5716. while (pTemp) {
  5717. if (EquivalentServers(pszLDAPServer, pTemp->pszLDAPServer)){
  5718. if (pTemp->fNoDataGot) {
  5719. //
  5720. // This is necessary for V2 server
  5721. // If BoundOk is not set we may end up not loading
  5722. // the default schema
  5723. //
  5724. fBoundOk = TRUE;
  5725. LEAVE_SUBSCHEMA_CRITSECT();
  5726. BAIL_ON_FAILURE(
  5727. hr = HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)
  5728. );
  5729. }
  5730. pRootDSE->fPagingSupported = pTemp->fPagingSupported;
  5731. pRootDSE->fSortingSupported = pTemp->fSortingSupported;
  5732. pRootDSE->fVLVSupported = pTemp->fVLVSupported;
  5733. pRootDSE->fAttribScopedSupported = pTemp->fAttribScopedSupported;
  5734. pRootDSE->dwSecDescType = pTemp->dwSecDescType;
  5735. pRootDSE->fDomScopeSupported = pTemp->fDomScopeSupported;
  5736. pRootDSE->fTalkingToAD = pTemp->fTalkingToAD;
  5737. pRootDSE->fTalkingToEnhancedAD = pTemp->fTalkingToEnhancedAD;
  5738. pRootDSE->fNoDataGot = pTemp->fNoDataGot;
  5739. pRootDSE->pszSubSchemaEntry = AllocADsStr(pTemp->pszSubSchemaEntry);
  5740. if (!pRootDSE->pszSubSchemaEntry) {
  5741. hr = E_OUTOFMEMORY;
  5742. }
  5743. LEAVE_SUBSCHEMA_CRITSECT();
  5744. //
  5745. // we have at least once bound successfully to the domain
  5746. //
  5747. fBoundOk = TRUE;
  5748. goto error; // can't return direct, need clean up
  5749. }
  5750. pTemp = pTemp->pNext;
  5751. }
  5752. LEAVE_SUBSCHEMA_CRITSECT();
  5753. hr = LdapOpenObject(
  5754. pszLDAPServer,
  5755. NULL,
  5756. &ld,
  5757. Credentials,
  5758. dwPort
  5759. );
  5760. BAIL_ON_FAILURE(hr);
  5761. //
  5762. // we have once bound to the node successfully - just now
  5763. //
  5764. fBoundOk=TRUE;
  5765. //
  5766. // Ask only for the attributes we are intersted in.
  5767. //
  5768. aStrings[0] = LDAP_OPATT_SUBSCHEMA_SUBENTRY_W;
  5769. aStrings[1] = LDAP_OPATT_SUPPORTED_CONTROL_W;
  5770. aStrings[2] = LDAP_OPATT_SUPPORTED_CAPABILITIES_W;
  5771. aStrings[3] = NULL;
  5772. hr = LdapSearchS(
  5773. ld,
  5774. NULL,
  5775. LDAP_SCOPE_BASE,
  5776. L"(objectClass=*)",
  5777. aStrings,
  5778. 0,
  5779. &res );
  5780. // Only one entry should be returned
  5781. if ( FAILED(hr)
  5782. || FAILED(hr = LdapFirstEntry( ld, res, &e ))
  5783. )
  5784. {
  5785. goto error;
  5786. }
  5787. hr = LdapGetValues(
  5788. ld,
  5789. e,
  5790. LDAP_OPATT_SUBSCHEMA_SUBENTRY_W,
  5791. &aValues1,
  5792. &nCount1
  5793. );
  5794. if (SUCCEEDED(hr) && nCount1==0) {
  5795. //
  5796. // No data flag indicates that we read nothing but the
  5797. // search was a success.
  5798. //
  5799. fNoData = TRUE;
  5800. }
  5801. BAIL_ON_FAILURE(hr);
  5802. hr = LdapGetValues(
  5803. ld,
  5804. e,
  5805. LDAP_OPATT_SUPPORTED_CONTROL_W,
  5806. &aValues2,
  5807. &nCount2
  5808. );
  5809. //
  5810. // okay to have no values for supportedControl
  5811. //
  5812. if (FAILED(hr)) {
  5813. //
  5814. // Reset the error because we were really succesful
  5815. // in reading critical information.
  5816. //
  5817. hr = S_OK;
  5818. }
  5819. hr = LdapGetValues(
  5820. ld,
  5821. e,
  5822. LDAP_OPATT_SUPPORTED_CAPABILITIES_W,
  5823. &aValues3,
  5824. &nCount3
  5825. );
  5826. //
  5827. // okay to have no values for supportedControl
  5828. //
  5829. if (FAILED(hr)) {
  5830. //
  5831. // Reset the error because we were really succesful
  5832. // in reading critical information.
  5833. //
  5834. hr = S_OK;
  5835. }
  5836. ENTER_SUBSCHEMA_CRITSECT();
  5837. pTemp = gpSubSchemaList;
  5838. while (pTemp) {
  5839. if (EquivalentServers(pszLDAPServer, pTemp->pszLDAPServer)) {
  5840. //
  5841. // Found a match -looks like someone has come in before us
  5842. //
  5843. if (pTemp->fNoDataGot) {
  5844. //
  5845. // This is necessary for V2 server
  5846. //
  5847. LEAVE_SUBSCHEMA_CRITSECT();
  5848. BAIL_ON_FAILURE(
  5849. hr = HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)
  5850. );
  5851. }
  5852. pRootDSE->fPagingSupported = pTemp->fPagingSupported;
  5853. pRootDSE->fSortingSupported = pTemp->fSortingSupported;
  5854. pRootDSE->fVLVSupported = pTemp->fVLVSupported;
  5855. pRootDSE->fAttribScopedSupported = pTemp->fAttribScopedSupported;
  5856. pRootDSE->dwSecDescType = pTemp->dwSecDescType;
  5857. pRootDSE->fDomScopeSupported = pTemp->fDomScopeSupported;
  5858. pRootDSE->fTalkingToAD = pTemp->fTalkingToAD;
  5859. pRootDSE->fTalkingToEnhancedAD = pTemp->fTalkingToEnhancedAD;
  5860. pRootDSE->fNoDataGot = pTemp->fNoDataGot;
  5861. pRootDSE->pszSubSchemaEntry = AllocADsStr(pTemp->pszSubSchemaEntry);
  5862. if (!pRootDSE->pszSubSchemaEntry) {
  5863. hr = E_OUTOFMEMORY;
  5864. }
  5865. LEAVE_SUBSCHEMA_CRITSECT();
  5866. goto error; // clean up first before return
  5867. }
  5868. pTemp = pTemp->pNext;
  5869. }
  5870. pNewNode = (PSCHEMALIST)AllocADsMem(sizeof(SCHEMALIST));
  5871. if (!pNewNode) {
  5872. hr = E_OUTOFMEMORY;
  5873. LEAVE_SUBSCHEMA_CRITSECT();
  5874. goto error; // clean up first before return
  5875. }
  5876. pNewNode->pNext = gpSubSchemaList;
  5877. pNewNode->pszLDAPServer = AllocADsStr(pszLDAPServer);
  5878. if (aValues1 && aValues1[0]) {
  5879. pNewNode->pszSubSchemaEntry = AllocADsStr(aValues1[0]);
  5880. pNewNode->fNoDataGot = FALSE;
  5881. }
  5882. else {
  5883. pNewNode->pszSubSchemaEntry = NULL;
  5884. pNewNode->fNoDataGot = TRUE;
  5885. }
  5886. //
  5887. // Default to this value
  5888. //
  5889. pNewNode->dwSecDescType = ADSI_LDAPC_SECDESC_NONE;
  5890. if (aValues2) {
  5891. for (int j=0; j<nCount2; j++) {
  5892. if (_wcsicmp(aValues2[j], LDAP_PAGED_RESULT_OID_STRING_W) == 0) {
  5893. pNewNode->fPagingSupported = TRUE;
  5894. }
  5895. else if (_wcsicmp(aValues2[j], LDAP_SERVER_SORT_OID_W) == 0) {
  5896. pNewNode->fSortingSupported = TRUE;
  5897. }
  5898. else if (_wcsicmp(aValues2[j], LDAP_SERVER_SD_FLAGS_OID_W) == 0) {
  5899. pNewNode->dwSecDescType = ADSI_LDAPC_SECDESC_NT;
  5900. }
  5901. else if (_wcsicmp(aValues2[j], ADSI_LDAP_OID_SECDESC_OLD) == 0) {
  5902. pNewNode->dwSecDescType = ADSI_LDAPC_SECDESC_OTHER;
  5903. }
  5904. else if (_wcsicmp(aValues2[j], LDAP_SERVER_DOMAIN_SCOPE_OID_W)
  5905. == 0) {
  5906. pNewNode->fDomScopeSupported = TRUE;
  5907. }
  5908. else if (_wcsicmp(aValues2[j], LDAP_CONTROL_VLVREQUEST_W) == 0) {
  5909. pNewNode->fVLVSupported = TRUE;
  5910. }
  5911. else if (_wcsicmp(aValues2[j], LDAP_SERVER_ASQ_OID_W) == 0) {
  5912. pNewNode->fAttribScopedSupported = TRUE;
  5913. }
  5914. }
  5915. }
  5916. else {
  5917. pNewNode->fPagingSupported = FALSE;
  5918. pNewNode->fSortingSupported = FALSE;
  5919. pNewNode->fDomScopeSupported = FALSE;
  5920. pNewNode->fVLVSupported = FALSE;
  5921. pNewNode->fAttribScopedSupported = FALSE;
  5922. }
  5923. if (aValues3) {
  5924. for (int j=0; j<nCount3; j++) {
  5925. if (_wcsicmp(aValues3[j], LDAP_CAP_ACTIVE_DIRECTORY_OID_W)
  5926. == 0) {
  5927. pNewNode->fTalkingToAD = TRUE;
  5928. }
  5929. else if (_wcsicmp(aValues3[j],
  5930. LDAP_CAP_ACTIVE_DIRECTORY_V51_OID_W)
  5931. == 0) {
  5932. //
  5933. // Replace with correct OID from ntldap.h.
  5934. //
  5935. pNewNode->fTalkingToEnhancedAD = TRUE;
  5936. }
  5937. }
  5938. }
  5939. else {
  5940. //
  5941. // Should already be false but just in case.
  5942. //
  5943. pNewNode->fTalkingToAD = FALSE;
  5944. pNewNode->fTalkingToEnhancedAD = FALSE;
  5945. }
  5946. gpSubSchemaList = pNewNode;
  5947. if (fNoData == FALSE) {
  5948. pRootDSE->fPagingSupported = pNewNode->fPagingSupported;
  5949. pRootDSE->fSortingSupported = pNewNode->fSortingSupported;
  5950. pRootDSE->fVLVSupported = pNewNode->fVLVSupported;
  5951. pRootDSE->fAttribScopedSupported = pNewNode->fAttribScopedSupported;
  5952. pRootDSE->fNoDataGot = pNewNode->fNoDataGot;
  5953. pRootDSE->dwSecDescType = pNewNode->dwSecDescType;
  5954. pRootDSE->fDomScopeSupported = pNewNode->fDomScopeSupported;
  5955. pRootDSE->fTalkingToAD = pNewNode->fTalkingToAD;
  5956. pRootDSE->fTalkingToEnhancedAD = pNewNode->fTalkingToEnhancedAD;
  5957. pRootDSE->pszSubSchemaEntry = AllocADsStr(pNewNode->pszSubSchemaEntry);
  5958. if (!pRootDSE->pszSubSchemaEntry) {
  5959. hr = E_OUTOFMEMORY;
  5960. }
  5961. }
  5962. LEAVE_SUBSCHEMA_CRITSECT();
  5963. error:
  5964. if (aValues1) {
  5965. LdapValueFree(aValues1);
  5966. }
  5967. if (aValues2) {
  5968. LdapValueFree(aValues2);
  5969. }
  5970. if (aValues3) {
  5971. LdapValueFree(aValues3);
  5972. }
  5973. if (res) {
  5974. LdapMsgFree(res);
  5975. }
  5976. if (ld) {
  5977. LdapCloseObject(ld);
  5978. }
  5979. //
  5980. // return to caller if we have at least once bound succsufully
  5981. // to the node
  5982. //
  5983. if (pfBoundOk)
  5984. *pfBoundOk = fBoundOk;
  5985. //
  5986. // Need to special case fNoData to ensure that the other code
  5987. // that relies on this eCode from this routine continues to
  5988. // work properly
  5989. //
  5990. if (fNoData) {
  5991. RRETURN(HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE));
  5992. } else
  5993. RRETURN(hr);
  5994. }