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

1012 lines
34 KiB

  1. #include "pch.h"
  2. #include "stddef.h"
  3. #pragma hdrstop
  4. /*-----------------------------------------------------------------------------
  5. / Query thread bits
  6. /----------------------------------------------------------------------------*/
  7. //
  8. // Page size used for paging the result sets (the LDAP server core is a sync process)
  9. // therefore getting it to return smaller result blobs is better for us
  10. //
  11. #define PAGE_SIZE 64
  12. #define MAX_RESULT 10000
  13. //
  14. // When the query is issued we always pull back at least ADsPath and objectClass
  15. // as properites (these are required for the viewer to work). Therefore these define
  16. // the mapping of these values to the returned column set.
  17. //
  18. #define PROPERTYMAP_ADSPATH 0
  19. #define PROPERTYMAP_OBJECTCLASS 1
  20. #define PROPERTYMAP_USER 2
  21. #define INDEX_TO_PROPERTY(i) (i+PROPERTYMAP_USER)
  22. //
  23. // THREADDATA this is the state structure for the thread, it encapsulates
  24. // the parameters and other junk required to the keep the thread alive.
  25. //
  26. typedef struct
  27. {
  28. LPTHREADINITDATA ptid;
  29. INT cProperties; // number of properties in aProperties
  30. LPWSTR* aProperties; // array of string pointers for "property names"
  31. INT cColumns; // number of columsn in view
  32. INT* aColumnToPropertyMap; // mapping from display column index to property name
  33. } THREADDATA, * LPTHREADDATA;
  34. //
  35. // Helper macro to send messages for the fg view, including the reference
  36. // count
  37. //
  38. #define SEND_VIEW_MESSAGE(ptid, uMsg, lParam) \
  39. SendMessage(GetParent(ptid->hwndView), uMsg, (ptid)->dwReference, lParam)
  40. //
  41. // Function prototypes for the query thread engine.
  42. //
  43. HRESULT QueryThread_IssueQuery(LPTHREADDATA ptd);
  44. HRESULT QueryThread_BuildPropertyList(LPTHREADDATA ptd);
  45. VOID QueryThread_FreePropertyList(LPTHREADDATA ptd);
  46. /*-----------------------------------------------------------------------------
  47. / Helper functions
  48. /----------------------------------------------------------------------------*/
  49. /*-----------------------------------------------------------------------------
  50. / QueryThread_GetFilter
  51. / ----------------------
  52. / Construct the LDAP filter we are going to use for this query.
  53. /
  54. / In:
  55. / ppQuery -> receives the full filter
  56. / pBaseFilter -> filter string to use as base
  57. / fShowHidden = show hidden objects?
  58. /
  59. / Out:
  60. / HRESULT
  61. /----------------------------------------------------------------------------*/
  62. VOID _GetFilter(LPWSTR pFilter, UINT* pcchFilterLen, LPWSTR pBaseFilter, BOOL fShowHidden)
  63. {
  64. TraceEnter(TRACE_QUERYTHREAD, "_GetFilter");
  65. if (pFilter)
  66. *pFilter = TEXT('\0');
  67. PutStringElementW(pFilter, pcchFilterLen, L"(&");
  68. if (!fShowHidden)
  69. PutStringElementW(pFilter, pcchFilterLen, c_szShowInAdvancedViewOnly);
  70. PutStringElementW(pFilter, pcchFilterLen, pBaseFilter);
  71. PutStringElementW(pFilter, pcchFilterLen, L")");
  72. TraceLeave();
  73. }
  74. HRESULT QueryThread_GetFilter(LPWSTR* ppFilter, LPWSTR pBaseFilter, BOOL fShowHidden)
  75. {
  76. HRESULT hr;
  77. UINT cchFilterLen = 0;
  78. TraceEnter(TRACE_QUERYTHREAD, "QueryThread_GetFilter");
  79. _GetFilter(NULL, &cchFilterLen, pBaseFilter, fShowHidden);
  80. hr = LocalAllocStringLenW(ppFilter, cchFilterLen);
  81. FailGracefully(hr, "Failed to allocate buffer for query string");
  82. _GetFilter(*ppFilter, NULL, pBaseFilter, fShowHidden);
  83. hr = S_OK;
  84. exit_gracefully:
  85. TraceLeaveResult(hr);
  86. }
  87. /*-----------------------------------------------------------------------------
  88. / Background query thread, this does the work of issuing the query and then
  89. / populating the view.
  90. /----------------------------------------------------------------------------*/
  91. /*-----------------------------------------------------------------------------
  92. / QueryThread
  93. / -----------
  94. / Thread function sits spinning its wheels waiting for a query to be
  95. / received from the outside world. The main result viewer communicates
  96. / with this code by ThreadSendMessage.
  97. /
  98. / In:
  99. / pThreadParams -> structure that defines out thread information
  100. /
  101. / Out:
  102. / -
  103. /----------------------------------------------------------------------------*/
  104. DWORD WINAPI QueryThread(LPVOID pThreadParams)
  105. {
  106. HRESULT hresCoInit;
  107. MSG msg;
  108. LPTHREADINITDATA pThreadInitData = (LPTHREADINITDATA)pThreadParams;
  109. THREADDATA td = {0};
  110. td.ptid = pThreadInitData;
  111. //td.cProperties = 0;
  112. //td.aProperties = NULL;
  113. //td.cColumns = 0;
  114. //td.aColumnToPropertyMap = NULL;
  115. hresCoInit = CoInitialize(NULL);
  116. FailGracefully(hresCoInit, "Failed to CoInitialize");
  117. GetActiveWindow(); // ensure we have amessage queue
  118. QueryThread_IssueQuery(&td);
  119. while (GetMessage(&msg, NULL, 0, 0) > 0)
  120. {
  121. switch (msg.message)
  122. {
  123. case RVTM_STOPQUERY:
  124. TraceMsg("RVTM_STOPQUERY received - ignoring");
  125. break;
  126. case RVTM_REFRESH:
  127. {
  128. td.ptid->dwReference = (DWORD)msg.wParam;
  129. QueryThread_IssueQuery(&td);
  130. break;
  131. }
  132. case RVTM_SETCOLUMNTABLE:
  133. {
  134. if (td.ptid->hdsaColumns)
  135. DSA_DestroyCallback(td.ptid->hdsaColumns, FreeColumnCB, NULL);
  136. td.ptid->dwReference = (DWORD)msg.wParam;
  137. td.ptid->hdsaColumns = (HDSA)msg.lParam;
  138. QueryThread_FreePropertyList(&td);
  139. QueryThread_IssueQuery(&td);
  140. break;
  141. }
  142. default:
  143. break;
  144. }
  145. }
  146. exit_gracefully:
  147. QueryThread_FreePropertyList(&td);
  148. QueryThread_FreeThreadInitData(&td.ptid);
  149. if (SUCCEEDED(hresCoInit))
  150. CoUninitialize();
  151. DllRelease();
  152. ExitThread(0);
  153. return 0; // BOGUS: not never called
  154. }
  155. /*-----------------------------------------------------------------------------
  156. / QueryThread_FreeThreadInitData
  157. / ------------------------------
  158. / Release the THREADINITDATA structure that we are given when the thread
  159. / is created.
  160. /
  161. / In:
  162. / pptid ->-> thread init data structure to be released
  163. /
  164. / Out:
  165. / -
  166. /----------------------------------------------------------------------------*/
  167. VOID QueryThread_FreeThreadInitData(LPTHREADINITDATA* pptid)
  168. {
  169. LPTHREADINITDATA ptid = *pptid;
  170. TraceEnter(TRACE_QUERYTHREAD, "QueryThread_FreeThreadInitData");
  171. if (ptid)
  172. {
  173. LocalFreeStringW(&ptid->pQuery);
  174. LocalFreeStringW(&ptid->pScope);
  175. if (ptid->hdsaColumns)
  176. DSA_DestroyCallback(ptid->hdsaColumns, FreeColumnCB, NULL);
  177. SecureLocalFreeStringW(&ptid->pServer);
  178. SecureLocalFreeStringW(&ptid->pUserName);
  179. SecureLocalFreeStringW(&ptid->pPassword);
  180. LocalFree((HLOCAL)ptid);
  181. *pptid = NULL;
  182. }
  183. TraceLeave();
  184. }
  185. /*-----------------------------------------------------------------------------
  186. / QueryThread_CheckForStopQuery
  187. / -----------------------------
  188. / Peek the message queue looking for a stop query message, if we
  189. / can find one then we must bail out.
  190. /
  191. / In:
  192. / ptd -> thread data structure
  193. /
  194. / Out:
  195. / fStopQuery
  196. /----------------------------------------------------------------------------*/
  197. BOOL QueryThread_CheckForStopQuery(LPTHREADDATA ptd)
  198. {
  199. BOOL fStopQuery = FALSE;
  200. MSG msg;
  201. TraceEnter(TRACE_QUERYTHREAD, "QueryThread_CheckForStopQuery");
  202. while (PeekMessage(&msg, NULL, RVTM_FIRST, RVTM_LAST, PM_REMOVE))
  203. {
  204. TraceMsg("Found a RVTM_ message in queue, stopping query");
  205. fStopQuery = TRUE;
  206. }
  207. TraceLeaveValue(fStopQuery);
  208. }
  209. /*-----------------------------------------------------------------------------
  210. / QueryThread_IssueQuery
  211. / ----------------------
  212. / Issue a query using the IDirectorySearch interface, this is a more performant
  213. / to the wire interface that issues the query directly. The code binds to
  214. / the scope object (the specified path) and then issues the LDAP query
  215. / pumping the results into the viewer as required.
  216. /
  217. / In:
  218. / ptd -> thread information structurre
  219. /
  220. / Out:
  221. / HRESULT
  222. /----------------------------------------------------------------------------*/
  223. HRESULT QueryThread_IssueQuery(LPTHREADDATA ptd)
  224. {
  225. HRESULT hr;
  226. DWORD dwres;
  227. LPTHREADINITDATA ptid = ptd->ptid;
  228. LPWSTR pQuery = NULL;
  229. INT cItems = 0, iColumn;
  230. INT cMaxResult = MAX_RESULT;
  231. BOOL fStopQuery = FALSE;
  232. IDirectorySearch* pDsSearch = NULL;
  233. LPWSTR pszTempPath = NULL;
  234. IDsDisplaySpecifier *pdds = NULL;
  235. ADS_SEARCH_HANDLE hSearch = NULL;
  236. ADS_SEARCHPREF_INFO prefInfo[3];
  237. ADS_SEARCH_COLUMN column;
  238. HDPA hdpaResults = NULL;
  239. LPQUERYRESULT pResult = NULL;
  240. WCHAR szBuffer[2048]; // MAX_URL_LENGHT
  241. INT resid;
  242. LPWSTR pColumnData = NULL;
  243. HKEY hkPolicy = NULL;
  244. TraceEnter(TRACE_QUERYTHREAD, "QueryThread_IssueQuery");
  245. // The foreground gave us a query so we are going to go and issue
  246. // it now, having done this we will then be able to stream the
  247. // result blobs back to the caller.
  248. hr = QueryThread_GetFilter(&pQuery, ptid->pQuery, ptid->fShowHidden);
  249. FailGracefully(hr, "Failed to build LDAP query from scope, parameters + filter");
  250. Trace(TEXT("Query is: %s"), pQuery);
  251. Trace(TEXT("Scope is: %s"), ptid->pScope);
  252. // Get the IDsDisplaySpecifier interface:
  253. hr = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
  254. FailGracefully(hr, "Failed to get the IDsDisplaySpecifier object");
  255. hr = pdds->SetServer(ptid->pServer, ptid->pUserName, ptid->pPassword, DSSSF_DSAVAILABLE);
  256. FailGracefully(hr, "Failed to server information");
  257. // initialize the query engine, specifying the scope, and the search parameters
  258. hr = QueryThread_BuildPropertyList(ptd);
  259. FailGracefully(hr, "Failed to build property array to query for");
  260. hr = AdminToolsOpenObject(ptid->pScope, ptid->pUserName, ptid->pPassword, ADS_SECURE_AUTHENTICATION, IID_PPV_ARG(IDirectorySearch, &pDsSearch));
  261. FailGracefully(hr, "Failed to get the IDirectorySearch interface for the given scope");
  262. prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; // sub-tree search
  263. prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
  264. prefInfo[0].vValue.Integer = ADS_SCOPE_SUBTREE;
  265. prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS; // async
  266. prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN;
  267. prefInfo[1].vValue.Boolean = TRUE;
  268. prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; // paged results
  269. prefInfo[2].vValue.dwType = ADSTYPE_INTEGER;
  270. prefInfo[2].vValue.Integer = PAGE_SIZE;
  271. hr = pDsSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));
  272. FailGracefully(hr, "Failed to set search preferences");
  273. hr = pDsSearch->ExecuteSearch(pQuery, ptd->aProperties, ptd->cProperties, &hSearch);
  274. FailGracefully(hr, "Failed in ExecuteSearch");
  275. // pick up the policy value which defines the max results we are going to use
  276. dwres = RegOpenKeyEx(HKEY_CURRENT_USER, DS_POLICY, 0, KEY_READ, &hkPolicy);
  277. if (ERROR_SUCCESS == dwres)
  278. {
  279. DWORD dwType, cbSize;
  280. dwres = RegQueryValueEx(hkPolicy, TEXT("QueryLimit"), NULL, &dwType, NULL, &cbSize);
  281. if ((ERROR_SUCCESS == dwres) && (dwType == REG_DWORD) && (cbSize == SIZEOF(cMaxResult)))
  282. {
  283. // checked the type and the size of the result above.
  284. RegQueryValueEx(hkPolicy, TEXT("QueryLimit"), NULL, NULL, (LPBYTE)&cMaxResult, &cbSize);
  285. }
  286. RegCloseKey(hkPolicy);
  287. }
  288. // issue the query, pumping the results to the foreground UI which
  289. // will inturn populate the the list view
  290. Trace(TEXT("Result limit set to %d"), cMaxResult);
  291. for (cItems = 0 ; cItems < cMaxResult;)
  292. {
  293. ADsSetLastError(ERROR_SUCCESS, NULL, NULL); // clear the ADSI previous errror
  294. hr = pDsSearch->GetNextRow(hSearch);
  295. fStopQuery = QueryThread_CheckForStopQuery(ptd);
  296. Trace(TEXT("fStopQuery %d, hr %08x"), fStopQuery, hr);
  297. if (fStopQuery || (hr == S_ADS_NOMORE_ROWS))
  298. {
  299. DWORD dwError;
  300. WCHAR wszError[64], wszName[64];
  301. hr = ADsGetLastError(&dwError, wszError, ARRAYSIZE(wszError), wszName, ARRAYSIZE(wszName));
  302. if (SUCCEEDED(hr) && (dwError != ERROR_MORE_DATA))
  303. {
  304. break;
  305. }
  306. hr = S_OK; // we have more data so lets continue
  307. continue;
  308. }
  309. FailGracefully(hr, "Failed in GetNextRow");
  310. cItems++;
  311. // We have a result, lets ensure that we have posted the blob
  312. // we are building before we start constructing a new one. We
  313. // send pages of items to the fg thread for it to add to the
  314. // result view, if the blob returns FALSE then we must tidy the
  315. // DPA before continuing.
  316. if (((cItems % 10) == 0) && hdpaResults) // 10 is a nice block size
  317. {
  318. TraceMsg("Posting results blob to fg thread");
  319. if (!SEND_VIEW_MESSAGE(ptid, DSQVM_ADDRESULTS, (LPARAM)hdpaResults))
  320. DPA_DestroyCallback(hdpaResults, FreeQueryResultCB, IntToPtr(ptd->cColumns));
  321. hdpaResults = NULL;
  322. }
  323. if (!hdpaResults)
  324. {
  325. hdpaResults = DPA_Create(PAGE_SIZE);
  326. TraceAssert(hdpaResults);
  327. if (!hdpaResults)
  328. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate result DPA");
  329. }
  330. // Add the result we have to the result blob, the first
  331. // two things we need are the class and the ADsPath of the
  332. // object, then loop over the properties to generate the
  333. // column data
  334. pResult = (LPQUERYRESULT)LocalAlloc(LPTR, SIZEOF(QUERYRESULT)+(SIZEOF(COLUMNVALUE)*(ptd->cColumns-1)));
  335. TraceAssert(pResult);
  336. if (pResult)
  337. {
  338. // Get the ADsPath and ObjectClass of the object, these must remain UNICODE as
  339. // they are used later for binding to the object. All other display information
  340. // can be fixed up later.
  341. pResult->iImage = -1;
  342. // get the ADsPath. If the provider is GC: then replace with LDAP: so that
  343. // when we interact with this object we are kept happy.
  344. hr = pDsSearch->GetColumn(hSearch, c_szADsPath, &column);
  345. FailGracefully(hr, "Failed to get the ADsPath column");
  346. hr = StringFromSearchColumn(&column, &pResult->pPath);
  347. pDsSearch->FreeColumn(&column);
  348. Trace(TEXT("Object path: %s"), pResult->pPath);
  349. if (SUCCEEDED(hr) &&
  350. ((pResult->pPath[0]== L'G') && (pResult->pPath[1] == L'C')))
  351. {
  352. TraceMsg("Replacing provider with LDAP:");
  353. int cchTempPath = lstrlenW(pResult->pPath)+3;
  354. hr = LocalAllocStringLenW(&pszTempPath, cchTempPath);
  355. if (SUCCEEDED(hr))
  356. {
  357. StrCpyNW(pszTempPath, c_szLDAP, cchTempPath);
  358. StrCatBuffW(pszTempPath, pResult->pPath+3, cchTempPath); // skip GC:
  359. LocalFreeStringW(&pResult->pPath);
  360. pResult->pPath = pszTempPath;
  361. }
  362. Trace(TEXT("New path is: %s"), pResult->pPath);
  363. }
  364. FailGracefully(hr, "Failed to get ADsPath from column");
  365. // get the objectClass
  366. hr = pDsSearch->GetColumn(hSearch, c_szObjectClass, &column);
  367. FailGracefully(hr, "Failed to get the objectClass column");
  368. hr = ObjectClassFromSearchColumn(&column, &pResult->pObjectClass);
  369. pDsSearch->FreeColumn(&column);
  370. FailGracefully(hr, "Failed to get object class from column");
  371. // Now ensure that we have the icon cache, and then walk the list of columns
  372. // getting the text that represents those.
  373. if (SUCCEEDED(pdds->GetIconLocation(pResult->pObjectClass, DSGIF_GETDEFAULTICON, szBuffer, ARRAYSIZE(szBuffer), &resid)))
  374. {
  375. pResult->iImage = Shell_GetCachedImageIndex(szBuffer, resid, 0x0);
  376. Trace(TEXT("Image index from shell is: %d"), pResult->iImage);
  377. }
  378. // is the class a container, mark this state into the result object
  379. pResult->fIsContainer = pdds->IsClassContainer(pResult->pObjectClass, pResult->pPath, 0x0);
  380. for (iColumn = 0 ; iColumn < ptd->cColumns ; iColumn++)
  381. {
  382. LPWSTR pProperty = ptd->aProperties[ptd->aColumnToPropertyMap[iColumn]];
  383. TraceAssert(pProperty);
  384. pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISUNDEFINED; // empty column
  385. hr = pDsSearch->GetColumn(hSearch, pProperty, &column);
  386. if ((hr != E_ADS_COLUMN_NOT_SET) && FAILED(hr))
  387. {
  388. Trace(TEXT("Failed to get column %d with code %08x"), iColumn, hr);
  389. hr = E_ADS_COLUMN_NOT_SET;
  390. }
  391. if (hr != E_ADS_COLUMN_NOT_SET)
  392. {
  393. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, iColumn);
  394. TraceAssert(pColumn);
  395. switch (pColumn->iPropertyType)
  396. {
  397. case PROPERTY_ISUNKNOWN:
  398. case PROPERTY_ISSTRING:
  399. case PROPERTY_ISDNSTRING:
  400. {
  401. // we are treating the property as a string, therefore convert the search
  402. // column to a string value and convert as required.
  403. pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISSTRING;
  404. if (pColumn->fHasColumnHandler)
  405. {
  406. // we have the CLSID for a column handler, therefore lets CoCreate it
  407. // and pass it to the ::GetText method.
  408. if (!pColumn->pColumnHandler)
  409. {
  410. TraceGUID("Attempting to create IDsQueryColumnHandler from GUID: ", pColumn->clsidColumnHandler);
  411. hr = CoCreateInstance(pColumn->clsidColumnHandler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDsQueryColumnHandler, &pColumn->pColumnHandler));
  412. if (SUCCEEDED(hr))
  413. hr = pColumn->pColumnHandler->Initialize(0x0, ptid->pServer, ptid->pUserName, ptid->pPassword);
  414. if (FAILED(hr))
  415. {
  416. TraceMsg("Failed to CoCreate the column handler, marking the column as not having one");
  417. pColumn->fHasColumnHandler = FALSE;
  418. pColumn->pColumnHandler = NULL;
  419. }
  420. }
  421. // if pColumnHandler != NULL, then call its ::GetText method, this is the string we should
  422. // then place into the column.
  423. if (pColumn->pColumnHandler)
  424. {
  425. pColumn->pColumnHandler->GetText(&column, szBuffer, ARRAYSIZE(szBuffer));
  426. LocalAllocStringW(&pResult->aColumn[iColumn].pszText, szBuffer);
  427. }
  428. }
  429. else
  430. {
  431. // if we were able to convert the column value to a string,
  432. // then lets pass it to the column handler (if there is one
  433. // to get the display string), or just copy this into the column
  434. // structure (thunking accordingly).
  435. if (SUCCEEDED(StringFromSearchColumn(&column, &pColumnData)))
  436. {
  437. LocalAllocStringW(&pResult->aColumn[iColumn].pszText, pColumnData);
  438. LocalFreeStringW(&pColumnData);
  439. }
  440. }
  441. break;
  442. }
  443. case PROPERTY_ISBOOL: // treat the BOOL as a number
  444. case PROPERTY_ISNUMBER:
  445. {
  446. // its a number, therefore lets pick up the number value from the
  447. // result and store that.
  448. pResult->aColumn[iColumn].iPropertyType = PROPERTY_ISNUMBER;
  449. pResult->aColumn[iColumn].iValue = column.pADsValues->Integer;
  450. break;
  451. }
  452. }
  453. pDsSearch->FreeColumn(&column);
  454. }
  455. }
  456. if (-1 == DPA_AppendPtr(hdpaResults, pResult))
  457. {
  458. FreeQueryResult(pResult, ptd->cColumns);
  459. LocalFree((HLOCAL)pResult);
  460. }
  461. pResult = NULL;
  462. }
  463. }
  464. hr = S_OK;
  465. exit_gracefully:
  466. Trace(TEXT("cItems %d, (hdpaResults %08x (%d))"), cItems, hdpaResults, hdpaResults ? DPA_GetPtrCount(hdpaResults):0);
  467. if (hdpaResults)
  468. {
  469. // As we send bunches of results to the fg thread check to see if we have a
  470. // DPA with any pending items in it, if we do then lets ensure we post that
  471. // off, if that succedes (the msg returns TRUE) then we are done, otherwise
  472. // hdpaResults needs to be free'd
  473. Trace(TEXT("Posting remaining results to fg thread (%d)"), DPA_GetPtrCount(hdpaResults));
  474. if (SEND_VIEW_MESSAGE(ptid, DSQVM_ADDRESULTS, (LPARAM)hdpaResults))
  475. hdpaResults = NULL;
  476. }
  477. if (!fStopQuery)
  478. {
  479. SEND_VIEW_MESSAGE(ptid, DSQVM_FINISHED, (cItems == MAX_RESULT));
  480. }
  481. if (pResult)
  482. {
  483. FreeQueryResult(pResult, ptd->cColumns);
  484. LocalFree((HLOCAL)pResult);
  485. }
  486. if (hSearch && pDsSearch)
  487. {
  488. pDsSearch->CloseSearchHandle(hSearch);
  489. }
  490. LocalFreeStringW(&pQuery);
  491. DoRelease(pDsSearch);
  492. DoRelease(pdds);
  493. QueryThread_FreePropertyList(ptd); // its void when we issue a new query
  494. TraceLeaveResult(hr);
  495. }
  496. /*-----------------------------------------------------------------------------
  497. / QueryThread_BuildPropertyList
  498. / -----------------------------
  499. / Given the column DSA construct the property maps and the property
  500. / list we are going to query for. Internaly we always query for
  501. / ADsPath and objectClass, so walk the columns and work out
  502. / how many extra properties above this we have, then build an
  503. / array of property names containing the unique properties.
  504. /
  505. / We also construct an index table that maps from a column index
  506. / to a property name.
  507. /
  508. / In:
  509. / ptd -> thread information structurre
  510. /
  511. / Out:
  512. / HRESULT
  513. /----------------------------------------------------------------------------*/
  514. HRESULT QueryThread_BuildPropertyList(LPTHREADDATA ptd)
  515. {
  516. HRESULT hr;
  517. LPTHREADINITDATA ptid = ptd->ptid;
  518. INT i, j;
  519. TraceEnter(TRACE_QUERYTHREAD, "QueryThread_BuildPropertyList");
  520. // Walk the list of columns and compute the properties that are unique to this
  521. // query and generate a table for them. First count the property table
  522. // based on the columns DSA
  523. ptd->cProperties = PROPERTYMAP_USER;
  524. ptd->aProperties = NULL;
  525. ptd->cColumns = DSA_GetItemCount(ptid->hdsaColumns);
  526. ptd->aColumnToPropertyMap = NULL;
  527. for (i = 0 ; i < DSA_GetItemCount(ptid->hdsaColumns); i++)
  528. {
  529. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, i);
  530. TraceAssert(pColumn);
  531. if (StrCmpW(pColumn->pProperty, c_szADsPath) &&
  532. StrCmpW(pColumn->pProperty, c_szObjectClass))
  533. {
  534. ptd->cProperties++;
  535. }
  536. }
  537. Trace(TEXT("cProperties %d"), ptd->cProperties);
  538. ptd->aProperties = (LPWSTR*)LocalAlloc(LPTR, SIZEOF(LPWSTR)*ptd->cProperties);
  539. ptd->aColumnToPropertyMap = (INT*)LocalAlloc(LPTR, SIZEOF(INT)*ptd->cColumns);
  540. if (!ptd->aProperties || !ptd->aColumnToPropertyMap)
  541. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate property array / display array");
  542. ptd->aProperties[PROPERTYMAP_ADSPATH] = c_szADsPath;
  543. ptd->aProperties[PROPERTYMAP_OBJECTCLASS] = c_szObjectClass;
  544. for (j = PROPERTYMAP_USER, i = 0 ; i < ptd->cColumns; i++)
  545. {
  546. LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(ptid->hdsaColumns, i);
  547. TraceAssert(pColumn);
  548. if (!StrCmpW(pColumn->pProperty, c_szADsPath))
  549. {
  550. ptd->aColumnToPropertyMap[i] = PROPERTYMAP_ADSPATH;
  551. }
  552. else if (!StrCmpW(pColumn->pProperty, c_szObjectClass))
  553. {
  554. ptd->aColumnToPropertyMap[i] = PROPERTYMAP_OBJECTCLASS;
  555. }
  556. else
  557. {
  558. ptd->aProperties[j] = pColumn->pProperty;
  559. ptd->aColumnToPropertyMap[i] = j++;
  560. }
  561. Trace(TEXT("Property: %s"), ptd->aProperties[ptd->aColumnToPropertyMap[i]]);
  562. }
  563. hr = S_OK;
  564. exit_gracefully:
  565. if (FAILED(hr))
  566. QueryThread_FreePropertyList(ptd);
  567. TraceLeaveResult(hr);
  568. }
  569. /*-----------------------------------------------------------------------------
  570. / QueryThread_FreePropertyList
  571. / ----------------------------
  572. / Release a previously allocated property list assocaited with the
  573. / given thread.
  574. /
  575. / In:
  576. / ptd -> thread information structurre
  577. /
  578. / Out:
  579. / VOID
  580. /----------------------------------------------------------------------------*/
  581. VOID QueryThread_FreePropertyList(LPTHREADDATA ptd)
  582. {
  583. TraceEnter(TRACE_QUERYTHREAD, "QueryThread_FreePropertyList");
  584. if (ptd->aProperties)
  585. LocalFree((HLOCAL)ptd->aProperties);
  586. if (ptd->aColumnToPropertyMap)
  587. LocalFree((HLOCAL)ptd->aColumnToPropertyMap);
  588. ptd->cProperties = 0;
  589. ptd->aProperties = NULL;
  590. ptd->cColumns = 0;
  591. ptd->aColumnToPropertyMap = NULL;
  592. TraceLeave();
  593. }
  594. /*-----------------------------------------------------------------------------
  595. / CQueryThreadCH
  596. / --------------
  597. / Query thread column handler, this is a generic one used to convert
  598. / properties based on the CLSID we are instantiated with.
  599. /----------------------------------------------------------------------------*/
  600. class CQueryThreadCH : public IDsQueryColumnHandler
  601. {
  602. private:
  603. LONG _cRef;
  604. CLSID _clsid;
  605. IADsPathname *_padp;
  606. IDsDisplaySpecifier *_pdds;
  607. DWORD _dwFlags;
  608. LPWSTR _pszServer;
  609. LPWSTR _pszUserName;
  610. LPWSTR _pszPassword;
  611. public:
  612. CQueryThreadCH(REFCLSID rCLSID);
  613. ~CQueryThreadCH();
  614. // *** IUnknown ***
  615. STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
  616. STDMETHOD_(ULONG, AddRef)();
  617. STDMETHOD_(ULONG, Release)();
  618. // *** IDsQueryColumnHandler ***
  619. STDMETHOD(Initialize)(THIS_ DWORD dwFlags, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword);
  620. STDMETHOD(GetText)(ADS_SEARCH_COLUMN* psc, LPWSTR pszBuffer, INT cchBuffer);
  621. };
  622. //
  623. // constructor
  624. //
  625. CQueryThreadCH::CQueryThreadCH(REFCLSID rCLSID) :
  626. _cRef(1),
  627. _padp(NULL),
  628. _pdds(NULL),
  629. _clsid(rCLSID),
  630. _dwFlags(0),
  631. _pszServer(NULL),
  632. _pszUserName(NULL),
  633. _pszPassword(NULL)
  634. {
  635. TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::CQueryThreadCH");
  636. TraceGUID("CLSID of property: ", rCLSID);
  637. DllAddRef();
  638. TraceLeave();
  639. }
  640. CQueryThreadCH::~CQueryThreadCH()
  641. {
  642. TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::~CQueryThreadCH");
  643. DoRelease(_padp); // free the name cracker
  644. DoRelease(_pdds);
  645. SecureLocalFreeStringW(&_pszServer);
  646. SecureLocalFreeStringW(&_pszUserName);
  647. SecureLocalFreeStringW(&_pszPassword);
  648. DllRelease();
  649. TraceLeave();
  650. }
  651. //
  652. // Handler IUnknown interface
  653. //
  654. ULONG CQueryThreadCH::AddRef()
  655. {
  656. return InterlockedIncrement(&_cRef);
  657. }
  658. ULONG CQueryThreadCH::Release()
  659. {
  660. TraceAssert( 0 != _cRef );
  661. ULONG cRef = InterlockedDecrement(&_cRef);
  662. if ( 0 == cRef )
  663. {
  664. delete this;
  665. }
  666. return cRef;
  667. }
  668. HRESULT CQueryThreadCH::QueryInterface(REFIID riid, void **ppv)
  669. {
  670. static const QITAB qit[] =
  671. {
  672. QITABENT(CQueryThreadCH, IDsQueryColumnHandler), // IID_IDsQueryColumnHandler
  673. {0, 0 },
  674. };
  675. return QISearch(this, qit, riid, ppv);
  676. }
  677. //
  678. // Handle creating an instance of IDsFolderProperties for talking to WAB
  679. //
  680. STDAPI CQueryThreadCH_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  681. {
  682. CQueryThreadCH *pqtch = new CQueryThreadCH(*poi->pclsid);
  683. if (!pqtch)
  684. return E_OUTOFMEMORY;
  685. HRESULT hr = pqtch->QueryInterface(IID_IUnknown, (void **)ppunk);
  686. pqtch->Release();
  687. return hr;
  688. }
  689. /*-----------------------------------------------------------------------------
  690. / IDsQueryColumnHandler
  691. /----------------------------------------------------------------------------*/
  692. STDMETHODIMP CQueryThreadCH::Initialize(THIS_ DWORD dwFlags, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword)
  693. {
  694. TraceEnter(TRACE_QUERYTHREAD, "CQueryThread::Initialize");
  695. SecureLocalFreeStringW(&_pszServer);
  696. SecureLocalFreeStringW(&_pszUserName);
  697. SecureLocalFreeStringW(&_pszPassword);
  698. // copy new parameters away
  699. _dwFlags = dwFlags;
  700. HRESULT hr = LocalAllocStringW(&_pszServer, pszServer);
  701. if (SUCCEEDED(hr))
  702. hr = LocalAllocStringW(&_pszUserName, pszUserName);
  703. if (SUCCEEDED(hr))
  704. hr = LocalAllocStringW(&_pszPassword, pszPassword);
  705. DoRelease(_pdds) // discard previous IDisplaySpecifier object
  706. TraceLeaveResult(hr);
  707. }
  708. STDMETHODIMP CQueryThreadCH::GetText(ADS_SEARCH_COLUMN* psc, LPWSTR pszBuffer, INT cchBuffer)
  709. {
  710. HRESULT hr;
  711. LPWSTR pValue = NULL;
  712. TraceEnter(TRACE_QUERYTHREAD, "CQueryThreadCH::GetText");
  713. if (!psc || !pszBuffer)
  714. ExitGracefully(hr, E_UNEXPECTED, "Bad parameters passed to handler");
  715. pszBuffer[0] = L'\0';
  716. if (IsEqualCLSID(_clsid, CLSID_PublishedAtCH) || IsEqualCLSID(_clsid, CLSID_MachineOwnerCH))
  717. {
  718. BOOL fPrefix = IsEqualCLSID(_clsid, CLSID_PublishedAtCH);
  719. LPCWSTR pszPath = psc->pADsValues[0].DNString;
  720. TraceAssert(pszPath != NULL);
  721. // convert the ADsPath into its canonical form which is easier for the user
  722. // to understand, CoCreate IADsPathname now instead of each time we call
  723. // PrettyifyADsPathname.
  724. if (!_padp)
  725. {
  726. hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IADsPathname, &_padp));
  727. FailGracefully(hr, "Failed to get IADsPathname interface");
  728. }
  729. if (FAILED(GetDisplayNameFromADsPath(pszPath, pszBuffer, cchBuffer, _padp, fPrefix)))
  730. {
  731. TraceMsg("Failed to get display name from path");
  732. StrCpyNW(pszBuffer, pszPath, cchBuffer);
  733. }
  734. hr = S_OK;
  735. }
  736. else if (IsEqualCLSID(_clsid, CLSID_ObjectClassCH))
  737. {
  738. // get a string from the search column, and then look up the friendly name of the
  739. // class from its display specifier
  740. hr = ObjectClassFromSearchColumn(psc, &pValue);
  741. FailGracefully(hr, "Failed to get object class from psc");
  742. if (!_pdds)
  743. {
  744. DWORD dwFlags = 0;
  745. hr = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&_pdds);
  746. FailGracefully(hr, "Failed to get IDsDisplaySpecifier interface");
  747. hr = _pdds->SetServer(_pszServer, _pszUserName, _pszPassword, DSSSF_DSAVAILABLE);
  748. FailGracefully(hr, "Failed when setting server for display specifier object");
  749. }
  750. _pdds->GetFriendlyClassName(pValue, pszBuffer, cchBuffer);
  751. }
  752. else if (IsEqualCLSID(_clsid, CLSID_MachineOwnerCH))
  753. {
  754. // convert the DN of the user object into a string that we can display
  755. }
  756. else if (IsEqualCLSID(_clsid, CLSID_MachineRoleCH))
  757. {
  758. // convert the userAccountControl value into something we can display for the user
  759. if (psc->dwADsType == ADSTYPE_INTEGER)
  760. {
  761. INT iType = psc->pADsValues->Integer; // pick out the type
  762. if ((iType >= 4096) && (iType <= 8191))
  763. {
  764. TraceMsg("Returning WKS/SRV string");
  765. LoadStringW(GLOBAL_HINSTANCE, IDS_WKSORSERVER, pszBuffer, cchBuffer);
  766. }
  767. else if (iType >= 8192)
  768. {
  769. TraceMsg("Returning DC string");
  770. LoadStringW(GLOBAL_HINSTANCE, IDS_DC, pszBuffer, cchBuffer);
  771. }
  772. else
  773. {
  774. Trace(TEXT("Unknown type %x"), iType);
  775. }
  776. }
  777. }
  778. else
  779. {
  780. ExitGracefully(hr, E_UNEXPECTED, "m_clsid specifies column type not supported");
  781. }
  782. hr = S_OK;
  783. exit_gracefully:
  784. LocalFreeStringW(&pValue);
  785. TraceLeaveResult(hr);
  786. }