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.

826 lines
31 KiB

  1. // Win2000Dom.cpp: implementation of the CWin2000Dom class.
  2. /*---------------------------------------------------------------------------
  3. File: Win2000Dom.cpp
  4. Comments: Implementation of Win2K object enumeration. This object enumerates
  5. members in any given container for Win2k domain. It
  6. returns all information about the object that user requested.
  7. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  8. Proprietary and confidential to Mission Critical Software, Inc.
  9. REVISION LOG ENTRY
  10. Revision By: Sham Chauthani
  11. Revised on 07/02/99 12:40:00
  12. ---------------------------------------------------------------------------
  13. */
  14. #include "stdafx.h"
  15. #include "EaLen.hpp"
  16. #include <adserr.h>
  17. #include "NT4Enum.h"
  18. #include "Win2KDom.h"
  19. #ifdef _DEBUG
  20. #define new DEBUG_NEW
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. //////////////////////////////////////////////////////////////////////
  25. // Construction/Destruction
  26. //////////////////////////////////////////////////////////////////////
  27. CWin2000Dom::CWin2000Dom()
  28. {
  29. }
  30. CWin2000Dom::~CWin2000Dom()
  31. {
  32. }
  33. //---------------------------------------------------------------------------
  34. // GetContainerEnum : Enumerates all the objects in a Container for a Domain.
  35. // It returns a pointer to IEnumVARIANT supporting object.
  36. // The object enumerates through variants that contain
  37. // BSTR's for the name of the objects.
  38. //---------------------------------------------------------------------------
  39. HRESULT CWin2000Dom::GetContainerEnum(
  40. BSTR sContainerName, //in- LDAP subpath of the container to enumerate
  41. BSTR sDomainName, //in- Domain where the container resides
  42. IEnumVARIANT **& ppVarEnum //out-IEnumVariant object that enumerates all objects.
  43. )
  44. {
  45. // Build the enumerator and go with it.
  46. IADs *pRoot=NULL;
  47. IADsContainer *pCont=NULL;
  48. IEnumVARIANT *pEnum=NULL,*pMbrEnum=NULL;
  49. // IDispatch *pDisp = NULL;
  50. BSTR bstrPath;
  51. VARIANT varDSRoot;
  52. // ULONG ulFetch=0;
  53. IADsMembers *pMbr;
  54. TCHAR adspath[MAX_PATH];
  55. WCHAR sPath[LEN_Path];
  56. VARIANT var;
  57. HRESULT hr;
  58. //Get the name of the root container for this domain.
  59. //Read the Root DSE from the default DS, which will be the DS for
  60. //the local domain. This will get us the name of the schema container,
  61. //which is stored in the "defaultNamingContext" operational attribute.
  62. wsprintf(sPath,L"LDAP://%ls/RootDSE", sDomainName);
  63. hr = ADsGetObject(sPath,
  64. IID_IADs,
  65. (void**)&pRoot);
  66. hr = pRoot->Get(TEXT("defaultNamingContext"),&varDSRoot);
  67. pRoot->Release();
  68. //Now bind to the builtin container.
  69. _tcscpy(adspath,TEXT("LDAP://"));
  70. wsprintf(sPath, L"%ls/%ls", sDomainName, sContainerName);
  71. _tcscat(adspath,sPath);
  72. if (wcslen(sContainerName))
  73. _tcscat(adspath,L",");
  74. _tcscat(adspath,varDSRoot.bstrVal);
  75. hr = ADsGetObject(adspath,IID_IADsContainer,(void**)&pCont);
  76. if ( FAILED( hr ) )
  77. return hr;
  78. // We are going to hint the container object to return all the stuff that we need in the Enumerator class
  79. LPWSTR pszArray[] = { L"name", L"objectClass", L"sAMAccountName", L"groupType" };
  80. DWORD dwNumber = sizeof(pszArray)/sizeof(LPWSTR);
  81. hr = ADsBuildVarArrayStr( pszArray, dwNumber, &var);
  82. hr = pCont->put_Hints( var );
  83. VariantClear(&var);
  84. hr = ADsBuildEnumerator(pCont,&pEnum);
  85. if ( SUCCEEDED(hr) )
  86. {
  87. *ppVarEnum = pEnum;
  88. }
  89. IADs * pADs = NULL;
  90. IADsGroup * pGrp = NULL;
  91. BSTR bstrClass;
  92. hr = pCont->QueryInterface(IID_IADs, (void**)&pADs);
  93. hr = pADs->get_Class(&bstrClass);
  94. if ( wcscmp(bstrClass, L"group") == 0)
  95. {
  96. hr = pADs->get_ADsPath(&bstrPath);
  97. if SUCCEEDED(hr) {
  98. //Enumerate the members of this group. Bind to the
  99. //group requesting IADsGroup
  100. //
  101. hr = ADsGetObject(bstrPath,IID_IADsGroup,(void**)&pGrp);
  102. //Retrieve the Members property - this is a container
  103. //that holds IADs objects for the members
  104. //
  105. hr = pGrp->Members(&pMbr);
  106. //Create another enumerator and enumerate the members of
  107. //the group.
  108. //
  109. hr = ADsBuildEnumerator((IADsContainer*)pMbr,&pMbrEnum);
  110. if ( SUCCEEDED(hr) )
  111. {
  112. *ppVarEnum = pMbrEnum;
  113. }
  114. ADsFreeEnumerator(pEnum);
  115. }
  116. }
  117. pCont->Release();
  118. return S_OK;
  119. }
  120. //---------------------------------------------------------------------------
  121. // GetEnumeration : Given the information this method returns all requested
  122. // values for a given object in a VARIANT containing
  123. // SAFEARRAY containing each of the property value that was
  124. // requested by the caller.
  125. //---------------------------------------------------------------------------
  126. HRESULT CWin2000Dom::GetEnumeration(
  127. BSTR sContainerName, //in- Container to enumerate ( LDAP sub path )
  128. BSTR sDomainName, //in- Domain where the container resides
  129. BSTR m_sQuery, //in- LDAP query string (for filtering)
  130. long attrCnt, //in- Number of properties requested.
  131. LPWSTR * sAttr, //in- Pointer to array of Property names.
  132. ADS_SEARCHPREF_INFO prefInfo, //in- Search preference info.
  133. BOOL bMultiVal, //in- Indicates whether to return multivalue props or not.
  134. IEnumVARIANT **& pVarEnum //out- IEnumVARIANT object that will enumerate all returned objects.
  135. )
  136. {
  137. // First get the full path to the container from the subpath that we have here.
  138. _bstr_t sAdsPath;
  139. _bstr_t sGrpDN;
  140. _bstr_t sQuery;
  141. _variant_t var, var2;
  142. IADs * pAds = NULL;
  143. int nCnt = 0;
  144. // IADsMembers * pMbr = NULL;
  145. // IADsGroup * pGrp = NULL;
  146. HRESULT hr;
  147. IDirectorySearch * pSearch = NULL;
  148. ADS_SEARCH_HANDLE hSearch = NULL;
  149. TNodeList * pList = new TNodeList();
  150. // int cnt = 0;
  151. bool cont = true;
  152. ADS_SEARCH_COLUMN col;
  153. BSTR sClass = NULL;
  154. if (!pList)
  155. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  156. // Get the dwfault naming context for this domain
  157. sAdsPath = L"LDAP://";
  158. sAdsPath += sDomainName;
  159. sAdsPath += L"/rootDSE";
  160. hr = ADsGetObject(sAdsPath, IID_IADs, (void**) &pAds);
  161. if ( SUCCEEDED(hr))
  162. {
  163. hr = pAds->Get(L"defaultNamingContext", &var);
  164. if ( SUCCEEDED(hr) )
  165. {
  166. if ( (wcsncmp(sContainerName, L"LDAP://", 7) != 0) && (wcsncmp(sContainerName, L"GC://", 5) != 0) )
  167. {
  168. // Partial path supplied so we will build the rest.
  169. if ( sContainerName && *sContainerName )
  170. {
  171. sAdsPath = L"LDAP://";
  172. sAdsPath += sDomainName;
  173. sAdsPath += L"/";
  174. sAdsPath += sContainerName;
  175. sAdsPath += L",";
  176. sAdsPath += var.bstrVal;
  177. }
  178. else
  179. {
  180. sAdsPath = L"LDAP://";
  181. sAdsPath += sDomainName;
  182. sAdsPath += L"/";
  183. sAdsPath += var.bstrVal;
  184. }
  185. }
  186. else
  187. // Full path so no need to build anything.
  188. sAdsPath = sContainerName;
  189. }
  190. if ( SUCCEEDED(hr) )
  191. {
  192. pAds->Release();
  193. pAds = NULL;
  194. hr = ADsGetObject(sAdsPath, IID_IADs, (void**) &pAds);
  195. }
  196. if (SUCCEEDED(hr) )
  197. {
  198. hr = pAds->get_Class(&sClass);
  199. }
  200. if ( SUCCEEDED(hr) )
  201. {
  202. if ( sClass && wcscmp(sClass, L"group") == 0 && prefInfo.vValue.Integer != ADS_SCOPE_BASE )
  203. {
  204. // If we're trying to enumerate the contents of a group,
  205. // Construct the DN for group and the LDAP path to whole directory
  206. hr = pAds->Get(L"distinguishedName", &var2);
  207. if ( SUCCEEDED(hr) )
  208. {
  209. sGrpDN = var2.bstrVal;
  210. sAdsPath = L"LDAP://";
  211. sAdsPath += sDomainName;
  212. sAdsPath += L"/";
  213. sAdsPath += var.bstrVal;
  214. // modify the query so that we have (& (memberOf=%s) (...) ) query
  215. sQuery = L"(&(memberOf=";
  216. sQuery += sGrpDN;
  217. sQuery += L") ";
  218. sQuery += m_sQuery;
  219. sQuery += L")";
  220. // Also the scope changes since we need to search the whole domain
  221. prefInfo.vValue.Integer = ADS_SCOPE_SUBTREE;
  222. hr = ADsGetObject(sAdsPath, IID_IDirectorySearch, (void**) &pSearch);
  223. }
  224. }
  225. else
  226. {
  227. sQuery = m_sQuery;
  228. hr = pAds->QueryInterface(IID_IDirectorySearch, (void**)&pSearch);
  229. }
  230. SysFreeString(sClass);
  231. }
  232. if ( SUCCEEDED(hr) )
  233. {
  234. hr = pSearch->SetSearchPreference(&prefInfo, 1);
  235. // Set the query to be paged query so that we can get the data larger than a 1000.
  236. ADS_SEARCHPREF_INFO prefInfo2;
  237. prefInfo2.dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
  238. prefInfo2.vValue.dwType = ADSTYPE_INTEGER;
  239. prefInfo2.vValue.Integer = 999;
  240. hr = pSearch->SetSearchPreference(&prefInfo2, 1);
  241. }
  242. if ( SUCCEEDED(hr) )
  243. {
  244. if ( (prefInfo.vValue.Integer == ADS_SCOPE_BASE) && (bMultiVal) &&
  245. (_wcsicmp(L"member", sAttr[0]) == 0) || (_wcsicmp(L"memberOf", sAttr[0]) == 0) ||
  246. (_wcsicmp(L"directReports", sAttr[0]) == 0) || (_wcsicmp(L"managedObjects", sAttr[0]) == 0))
  247. {
  248. DoRangeQuery(sDomainName, sQuery, sAttr, attrCnt, hSearch, pSearch, bMultiVal, pList);
  249. }
  250. else
  251. {
  252. hr = pSearch->ExecuteSearch(sQuery, sAttr, attrCnt, &hSearch);
  253. if ( SUCCEEDED(hr) )
  254. {
  255. hr = pSearch->GetFirstRow(hSearch);
  256. }
  257. if ( hr == S_OK )
  258. {
  259. while( cont )
  260. {
  261. _variant_t * varAr = new _variant_t[attrCnt];
  262. if (!varAr)
  263. {
  264. if ( pSearch )
  265. {
  266. pSearch->CloseSearchHandle(hSearch);
  267. pSearch->Release();
  268. }
  269. if ( pAds )
  270. pAds->Release();
  271. delete pList;
  272. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  273. }
  274. // int ncol = 0;
  275. for ( long dw = 0; dw < attrCnt; dw++ )
  276. {
  277. hr = pSearch->GetColumn( hSearch, sAttr[dw], &col );
  278. if ( SUCCEEDED(hr) )
  279. {
  280. if ( col.dwNumValues > 0 )
  281. {
  282. // Get the type of attribute and put the value into the variant
  283. // and then into the Enumerator object.
  284. if ( col.dwNumValues < 2 || !bMultiVal )
  285. // put the last item into the enumeration.(memberOf=CN=DNSAdmins,CN=USERS,DC=devblewerg,DC=com)
  286. AttrToVariant(col.pADsValues[col.dwNumValues - 1], varAr[dw]);
  287. else
  288. {
  289. // Build a VARIANT array of all the values.
  290. SAFEARRAY * pArray;
  291. SAFEARRAYBOUND bd = {col.dwNumValues, 0};
  292. _variant_t var;
  293. _bstr_t strTemp;
  294. _variant_t HUGEP * vArray;
  295. pArray = SafeArrayCreate(VT_VARIANT|VT_BSTR, 1, &bd);
  296. // Fill up the VARIANT Array
  297. SafeArrayAccessData(pArray, (void HUGEP **) &vArray);
  298. for ( DWORD x = 0; x < col.dwNumValues; x++ )
  299. {
  300. nCnt++;
  301. AttrToVariant(col.pADsValues[x], var);
  302. strTemp = var;
  303. vArray[x] = _variant_t(strTemp);
  304. }
  305. SafeArrayUnaccessData(pArray);
  306. varAr[dw].vt = VT_ARRAY | VT_VARIANT;
  307. SafeArrayCopy(pArray, &varAr[dw].parray);
  308. }
  309. }
  310. else
  311. {
  312. // Put an empty string here.
  313. varAr[dw] = (BSTR)NULL;
  314. }
  315. pSearch->FreeColumn( &col );
  316. }
  317. else
  318. {
  319. // Put an empty string here.
  320. varAr[dw] = (BSTR)NULL;
  321. }
  322. }
  323. TAttrNode * pNode = new TAttrNode(attrCnt, varAr);
  324. if (!pNode)
  325. {
  326. delete [] varAr;
  327. if ( pSearch )
  328. {
  329. pSearch->CloseSearchHandle(hSearch);
  330. pSearch->Release();
  331. }
  332. if ( pAds )
  333. pAds->Release();
  334. delete pList;
  335. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  336. }
  337. // Clear the array
  338. delete [] varAr;
  339. if ( pSearch->GetNextRow(hSearch) == S_ADS_NOMORE_ROWS )
  340. cont = false;
  341. pList->InsertBottom(pNode);
  342. }
  343. }
  344. pSearch->CloseSearchHandle(hSearch);
  345. }
  346. }
  347. }
  348. if ( pSearch )
  349. pSearch->Release();
  350. if ( pAds )
  351. pAds->Release();
  352. // UpdateAccountInList(pList, sDomainName);
  353. *pVarEnum = (IEnumVARIANT *) new CNT4Enum(pList);
  354. if (!(*pVarEnum))
  355. {
  356. delete pList;
  357. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  358. }
  359. return S_OK;
  360. }
  361. //--------------------------------------------------------------------
  362. // AttrToVariant : This function stores a value from ADSValue struct
  363. // into a variant in a appropriate type.
  364. //--------------------------------------------------------------------
  365. bool CWin2000Dom::AttrToVariant(
  366. ADSVALUE pADsValues, //in- Value for a property in ADSVALUE struct
  367. _variant_t& var //out-Variant filled with info from the in parameter.
  368. )
  369. {
  370. HRESULT hr = S_OK;
  371. // Fill in the values as per the varset.
  372. switch (pADsValues.dwType)
  373. {
  374. case ADSTYPE_INVALID : break;
  375. case ADSTYPE_DN_STRING : var = pADsValues.DNString;
  376. break;
  377. case ADSTYPE_CASE_EXACT_STRING : var = pADsValues.CaseExactString;
  378. break;
  379. case ADSTYPE_CASE_IGNORE_STRING : var = pADsValues.CaseIgnoreString;
  380. break;
  381. case ADSTYPE_PRINTABLE_STRING : var = pADsValues.PrintableString;
  382. break;
  383. case ADSTYPE_NUMERIC_STRING : var = pADsValues.NumericString;
  384. break;
  385. case ADSTYPE_INTEGER : var.vt = VT_I4;
  386. var.lVal = pADsValues.Integer;
  387. break;
  388. case ADSTYPE_OCTET_STRING : {
  389. var.vt = VT_ARRAY | VT_UI1;
  390. var.parray = NULL;
  391. byte * pData;
  392. DWORD dwLength = pADsValues.OctetString.dwLength;
  393. SAFEARRAY * sA;
  394. SAFEARRAYBOUND rgBound = {dwLength, 0};
  395. sA = ::SafeArrayCreate(VT_UI1, 1, &rgBound);
  396. ::SafeArrayAccessData( sA, (void**)&pData);
  397. for ( DWORD i = 0; i < dwLength; i++ )
  398. pData[i] = pADsValues.OctetString.lpValue[i];
  399. hr = ::SafeArrayUnaccessData(sA);
  400. hr = ::SafeArrayCopy(sA, &var.parray);
  401. hr = ::SafeArrayDestroy(sA);
  402. }
  403. break;
  404. /* case ADSTYPE_UTC_TIME : var = L"Date not supported.";
  405. break;
  406. case ADSTYPE_LARGE_INTEGER : var = L"Large Integer not supported.";
  407. break;
  408. case ADSTYPE_PROV_SPECIFIC : var = L"Provider specific strings not supported.";
  409. break;
  410. case ADSTYPE_OBJECT_CLASS : var = pADsValues.ClassName;
  411. break;
  412. case ADSTYPE_CASEIGNORE_LIST : wcscat(sKeyName,L".ERROR");
  413. var.vt = VT_BSTR;
  414. var.bstrVal = L"Case ignore lists are not supported.";
  415. break;
  416. case ADSTYPE_OCTET_LIST : wcscat(sKeyName,L".ERROR");
  417. var.vt = VT_BSTR;
  418. var.bstrVal = L"Octet lists are not supported.";
  419. break;
  420. case ADSTYPE_PATH : wcscat(sKeyName,L".ERROR");
  421. var.vt = VT_BSTR;
  422. var.bstrVal = L"Path type not supported.";
  423. break;
  424. case ADSTYPE_POSTALADDRESS : wcscat(sKeyName,L".ERROR");
  425. var.vt = VT_BSTR;
  426. var.bstrVal = L"Postal addresses are not supported.";
  427. break;
  428. case ADSTYPE_TIMESTAMP : var.vt = VT_UI4;
  429. var.lVal = attrInfo.pADsValues[dw].UTCTime;
  430. break;
  431. case ADSTYPE_BACKLINK : wcscat(sKeyName,L".ERROR");
  432. var.vt = VT_BSTR;
  433. var.bstrVal = L"Backlink is not supported.";
  434. break;
  435. case ADSTYPE_TYPEDNAME : wcscat(sKeyName,L".ERROR");
  436. var.vt = VT_BSTR;
  437. var.bstrVal = L"Typed name not supported.";
  438. break;
  439. case ADSTYPE_HOLD : wcscat(sKeyName,L".ERROR");
  440. var.vt = VT_BSTR;
  441. var.bstrVal = L"Hold not supported.";
  442. break;
  443. case ADSTYPE_NETADDRESS : wcscat(sKeyName,L".ERROR");
  444. var.vt = VT_BSTR;
  445. var.bstrVal = L"NetAddress not supported.";
  446. break;
  447. case ADSTYPE_REPLICAPOINTER : wcscat(sKeyName,L".ERROR");
  448. var.vt = VT_BSTR;
  449. var.bstrVal = L"Replica pointer not supported.";
  450. break;
  451. case ADSTYPE_FAXNUMBER : wcscat(sKeyName,L".ERROR");
  452. var.vt = VT_BSTR;
  453. var.bstrVal = L"Faxnumber not supported.";
  454. break;
  455. case ADSTYPE_EMAIL : wcscat(sKeyName,L".ERROR");
  456. var.vt = VT_BSTR;
  457. var.bstrVal = L"Email not supported.";
  458. break;
  459. case ADSTYPE_NT_SECURITY_DESCRIPTOR : wcscat(sKeyName,L".ERROR");
  460. var.vt = VT_BSTR;
  461. var.bstrVal = L"Security Descriptor not supported.";
  462. break;
  463. */
  464. default : return false;
  465. }
  466. return true;
  467. }
  468. /*void CWin2000Dom::UpdateAccountInList(TNodeList *pList, BSTR sDomainName)
  469. {
  470. bool found = false;
  471. for ( TAttrNode * pNode = (TAttrNode *)pList->Head(); pNode; pNode = (TAttrNode *)pNode->Next())
  472. {
  473. if ( _bstr_t(pNode->m_Val) == _bstr_t(sPDC) )
  474. {
  475. found = true;
  476. break;
  477. }
  478. }
  479. if ( !found )
  480. {
  481. TAttrNode * pNode = new TAttrNode(attrCnt, varAr);
  482. pList->InsertBottom(pNode);
  483. }
  484. }
  485. */
  486. HRESULT CWin2000Dom::DoRangeQuery(BSTR sDomainName, BSTR sQuery, LPWSTR * sAttr, int attrCnt, ADS_SEARCH_HANDLE hSearch, IDirectorySearch * pSearch, BOOL bMultiVal, TNodeList * pList)
  487. {
  488. HRESULT hr;
  489. bool cont = true;
  490. ADS_SEARCH_COLUMN col;
  491. int nCnt = 0;
  492. int * pStartWith;
  493. int * pEndWith;
  494. WCHAR sAttrRange[LEN_Path];
  495. int tryCols = 0;
  496. LPWSTR * sAttrs = NULL;
  497. _variant_t * varAr;
  498. TAttrNode * pNode;
  499. LPWSTR * psAttrNames;
  500. BOOL * pDone;
  501. BOOL bAllDone = FALSE;
  502. int nOrigCnt = attrCnt;
  503. int ndx;
  504. pStartWith = new int[attrCnt];
  505. pEndWith = new int[attrCnt];
  506. psAttrNames = new LPWSTR[attrCnt];
  507. sAttrs = new LPWSTR[attrCnt];
  508. pDone = new BOOL[attrCnt];
  509. if ((!pStartWith) || (!pEndWith) || (!psAttrNames) || (!sAttrs) || (!pDone))
  510. {
  511. if (pStartWith)
  512. delete [] pStartWith;
  513. if (pEndWith)
  514. delete [] pEndWith;
  515. if (psAttrNames)
  516. delete [] psAttrNames;
  517. if (sAttrs)
  518. delete [] sAttrs;
  519. if (pDone)
  520. delete [] pDone;
  521. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  522. }
  523. for (ndx = 0; ndx < attrCnt; ndx++)
  524. {
  525. pStartWith[ndx] = 0;
  526. pEndWith[ndx] = 0;
  527. psAttrNames[ndx] = sAttr[ndx];
  528. sAttrs[ndx] = _wcsdup(sAttr[ndx]);
  529. pDone[ndx] = FALSE;
  530. }
  531. // continue to retrieve field's values in MAX chunks until done
  532. while (!bAllDone)
  533. {
  534. int last = 0;
  535. for (ndx = 0; ndx < attrCnt; ndx++)
  536. {
  537. if (pDone[ndx] == FALSE)
  538. {
  539. if (IsPropMultiValued((WCHAR*)psAttrNames[ndx], (WCHAR*)sDomainName) == true)
  540. wsprintf(sAttrRange, L"%s;range=%d-*", (WCHAR*)(psAttrNames[ndx]), pStartWith[ndx]);
  541. else
  542. wcscpy(sAttrRange, (WCHAR*)psAttrNames[ndx]);
  543. free(sAttrs[ndx]);
  544. sAttrs[last] = _wcsdup(sAttrRange);
  545. psAttrNames[last] = psAttrNames[ndx];
  546. pStartWith[last] = pStartWith[ndx];
  547. pEndWith[last] = pEndWith[ndx];
  548. pDone[last] = pDone[ndx];
  549. last++;
  550. }
  551. else
  552. free(sAttrs[ndx]);
  553. }
  554. attrCnt = last;
  555. varAr = new _variant_t[attrCnt];
  556. if (!varAr)
  557. {
  558. delete [] pStartWith;
  559. delete [] pEndWith;
  560. delete [] psAttrNames;
  561. delete [] pDone;
  562. for (ndx = 0; ndx < attrCnt; ndx++)
  563. {
  564. free(sAttrs[ndx]);
  565. }
  566. delete [] sAttrs;
  567. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  568. }
  569. for (ndx=0; ndx<attrCnt; ndx++)
  570. {
  571. varAr[ndx] = (BSTR)NULL;
  572. pDone[ndx] = TRUE;
  573. }
  574. hr = pSearch->ExecuteSearch(sQuery, sAttrs, attrCnt, &hSearch);
  575. if ( SUCCEEDED(hr) )
  576. {
  577. hr = pSearch->GetFirstRow(hSearch);
  578. }
  579. if ( hr == S_OK )
  580. {
  581. while( cont )
  582. {
  583. LPWSTR pszColumn;
  584. _bstr_t sTemp;
  585. //since the column name could have changed (in the case that there are
  586. //more values to enumerate than IDirectorySearch can in a single call :
  587. //default is 1000) we need to find the column's new name
  588. hr = pSearch->GetNextColumnName(hSearch, &pszColumn);
  589. while (pszColumn != NULL)
  590. {
  591. int current = -1;
  592. if ((SUCCEEDED(hr)) && (hr != S_ADS_NOMORE_COLUMNS))
  593. {
  594. //get the new column name
  595. do
  596. {
  597. current++;
  598. sTemp = psAttrNames[current];
  599. if (wcslen(psAttrNames[current]) != (wcslen(pszColumn)))
  600. sTemp += L";range=";
  601. }
  602. while ((current < attrCnt) && (wcsstr(pszColumn, (WCHAR*)sTemp) == NULL));
  603. pDone[current] = FALSE;
  604. if (wcsstr(pszColumn, (WCHAR*)sTemp) != NULL)
  605. {
  606. _bstr_t oldName = psAttrNames[current];
  607. oldName += L";range=";
  608. if ((wcsstr(pszColumn, oldName) != NULL) &&
  609. (wcsstr(pszColumn, L"-*") == NULL))
  610. {
  611. WCHAR sName[MAX_PATH];
  612. //now get the new range max retrieved so far
  613. swscanf(pszColumn, L"%[^;];range=%d-%d", sName, &pStartWith[current], &pEndWith[current]);
  614. free(sAttrs[current]);
  615. sAttrs[current] = _wcsdup(pszColumn); //save the new column name
  616. }
  617. else if ((wcsstr(pszColumn, L"-*") != NULL) || (!wcscmp(pszColumn, psAttrNames[current])))
  618. pDone[current] = TRUE;
  619. FreeADsMem(pszColumn);
  620. }
  621. }
  622. hr = pSearch->GetColumn( hSearch, sAttrs[current], &col );
  623. if ( SUCCEEDED(hr) )
  624. {
  625. if ( col.dwNumValues > 0 )
  626. {
  627. // Build a VARIANT array of all the values.
  628. SAFEARRAY * pArray;
  629. SAFEARRAYBOUND bd = {col.dwNumValues, 0};
  630. _variant_t var;
  631. _bstr_t strTemp;
  632. _variant_t HUGEP * vArray;
  633. pArray = SafeArrayCreate(VT_VARIANT|VT_BSTR, 1, &bd);
  634. // Fill up the VARIANT Array
  635. SafeArrayAccessData(pArray, (void HUGEP **) &vArray);
  636. for ( DWORD x = 0; x < col.dwNumValues; x++ )
  637. {
  638. nCnt++;
  639. AttrToVariant(col.pADsValues[x], var);
  640. strTemp = var;
  641. vArray[x] = _variant_t(strTemp);
  642. }
  643. SafeArrayUnaccessData(pArray);
  644. varAr[current].vt = VT_ARRAY | VT_VARIANT;
  645. SafeArrayCopy(pArray, &varAr[current].parray);
  646. }
  647. pSearch->FreeColumn( &col );
  648. }
  649. hr = pSearch->GetNextColumnName(hSearch, &pszColumn);
  650. }//end while more columns
  651. if ( pSearch->GetNextRow(hSearch) == S_ADS_NOMORE_ROWS )
  652. {
  653. hr = S_OK;
  654. cont = false;
  655. }
  656. }
  657. }
  658. else
  659. {
  660. bAllDone = TRUE;
  661. }
  662. if ( pStartWith[0] == 0 )
  663. {
  664. pNode = new TAttrNode((long)attrCnt, varAr);
  665. if (!pNode)
  666. {
  667. delete [] varAr;
  668. delete [] pStartWith;
  669. delete [] pEndWith;
  670. delete [] psAttrNames;
  671. delete [] pDone;
  672. for (ndx = 0; ndx < attrCnt; ndx++)
  673. {
  674. free(sAttrs[ndx]);
  675. }
  676. delete [] sAttrs;
  677. pSearch->CloseSearchHandle(hSearch);
  678. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  679. }
  680. }
  681. else //else add the new values for any remaining attributes
  682. {
  683. for (int i=0; i<attrCnt; i++)
  684. {
  685. int j=-1;
  686. bool bFound = false;
  687. //find the original column of this attribute for the 'Add' call
  688. while ((j < nOrigCnt) && (!bFound))
  689. {
  690. j++;
  691. if (wcscmp(psAttrNames[i], sAttr[j]) == 0)
  692. bFound = true; //original column number
  693. }
  694. if (bFound) //if found, add the new values for that column
  695. pNode->Add((long)j, (long)i, varAr);
  696. }
  697. }
  698. // Clear the array
  699. delete [] varAr;
  700. pSearch->CloseSearchHandle(hSearch);
  701. cont = true;
  702. bAllDone = TRUE;
  703. for (ndx = 0; ndx < attrCnt; ndx++)
  704. {
  705. pStartWith[ndx] = pEndWith[ndx] + 1; //start the next query
  706. bAllDone = ((bAllDone) && (pDone[ndx])) ? TRUE : FALSE; //see if done with all properties
  707. }
  708. }
  709. delete [] pStartWith;
  710. delete [] pEndWith;
  711. delete [] psAttrNames;
  712. delete [] pDone;
  713. for (ndx = 0; ndx < attrCnt; ndx++)
  714. {
  715. free(sAttrs[ndx]);
  716. }
  717. delete [] sAttrs;
  718. pList->InsertBottom(pNode);
  719. return hr;
  720. }
  721. /*********************************************************************
  722. * *
  723. * Written by: Paul Thompson *
  724. * Date: 10 NOV 2000 *
  725. * *
  726. * This function is responsible for checking a property's schema *
  727. * to see if that property is multi-valued or not. *
  728. * *
  729. *********************************************************************/
  730. //BEGIN IsPropMultiValued
  731. bool CWin2000Dom::IsPropMultiValued(const WCHAR * sPropName, const WCHAR * sDomain)
  732. {
  733. HRESULT hr;
  734. VARIANT_BOOL bMulti = VARIANT_FALSE;
  735. WCHAR sAdsPath[LEN_Path];
  736. IADsProperty * pProp = NULL;
  737. if ( wcslen(sPropName) == 0 )
  738. return false;
  739. //prepare to call the property's schema
  740. wcscpy(sAdsPath, L"LDAP://");
  741. wcscat(sAdsPath, sDomain);
  742. wcscat(sAdsPath, L"/");
  743. wcscat(sAdsPath, sPropName);
  744. wcscat(sAdsPath, L", schema");
  745. hr = ADsGetObject(sAdsPath, IID_IADsProperty, (void **)&pProp);
  746. // Get the Multi-Valued flag for the property
  747. if (SUCCEEDED(hr))
  748. {
  749. hr = pProp->get_MultiValued(&bMulti);
  750. pProp->Release();
  751. }
  752. if (bMulti == VARIANT_TRUE)
  753. return true;
  754. else
  755. return false;
  756. }
  757. //END IsPropMultiValued