Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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