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.

604 lines
19 KiB

  1. #include "pch.h"
  2. #include <dsgetdc.h> // DsGetDCName and DS structures
  3. #include <lm.h>
  4. #pragma hdrstop
  5. /*-----------------------------------------------------------------------------
  6. / GetGlobalCatalogPath
  7. / --------------------
  8. / Look up the GC using DsGcDcName and return a string containing the path.
  9. /
  10. / In:
  11. / pszServer, server to get the path for
  12. / pszBuffer, cchBuffer = buffer to fill
  13. /
  14. / Out:
  15. / HRESULT
  16. /----------------------------------------------------------------------------*/
  17. #define GC_PREFIX L"GC://"
  18. #define CCH_GC_PREFIX 5
  19. // NOTE: ChandanaS says use LocalFree when building for Win95 as the
  20. // NetApiBufferFree is not available, she will fix this in
  21. // time.
  22. #if !defined(UNICODE)
  23. #define NetApiBufferFree(x) LocalFree(x)
  24. #endif
  25. HRESULT GetGlobalCatalogPath(LPCWSTR pszServer, LPWSTR pszPath, INT cchBuffer)
  26. {
  27. HRESULT hres;
  28. DWORD dwres;
  29. PDOMAIN_CONTROLLER_INFOW pdci = NULL;
  30. ULONG uFlags = DS_RETURN_DNS_NAME|DS_DIRECTORY_SERVICE_REQUIRED;
  31. USES_CONVERSION;
  32. TraceEnter(TRACE_SCOPES, "GetGlobalCatalogPath");
  33. dwres = DsGetDcNameW(pszServer, NULL, NULL, NULL, uFlags, &pdci);
  34. if ( ERROR_NO_SUCH_DOMAIN == dwres )
  35. {
  36. TraceMsg("Trying with rediscovery bit set");
  37. dwres = DsGetDcNameW(pszServer, NULL, NULL, NULL, uFlags|DS_FORCE_REDISCOVERY, &pdci);
  38. }
  39. if ( (NO_ERROR != dwres) || !pdci->DnsForestName )
  40. ExitGracefully(hres, E_UNEXPECTED, "Failed to find the GC");
  41. if ( (lstrlenW(pdci->DnsForestName)+CCH_GC_PREFIX) > cchBuffer )
  42. ExitGracefully(hres, E_UNEXPECTED, "Buffer too small for the GC path");
  43. StrCpyW(pszPath, GC_PREFIX);
  44. StrCatW(pszPath, pdci->DnsForestName);
  45. Trace(TEXT("Resulting GC path is: %s"), W2T(pszPath));
  46. hres = S_OK;
  47. exit_gracefully:
  48. NetApiBufferFree(pdci);
  49. TraceLeaveResult(hres);
  50. }
  51. /*-----------------------------------------------------------------------------
  52. / Scope handling
  53. /----------------------------------------------------------------------------*/
  54. typedef struct
  55. {
  56. LPSCOPETHREADDATA ptd; // thread data structure
  57. INT index; // insert index into the visible scope list
  58. INT cScopes; // number of items enumerated
  59. LPWSTR pszDefaultDnsDomain; // default domain to be selected
  60. } ENUMSTATE, * LPENUMSTATE;
  61. /*-----------------------------------------------------------------------------
  62. / _ScopeProc
  63. / ----------
  64. / Handle scope messages from for the scope blocks we have allocated.
  65. /
  66. / In:
  67. / pScope -> refernce to scope block
  68. / uMsg = message
  69. / pVoid = arguments to message / = NULL
  70. /
  71. / Out:
  72. / HRESULT
  73. /----------------------------------------------------------------------------*/
  74. HRESULT CALLBACK _ScopeProc(LPCQSCOPE pScope, UINT uMsg, LPVOID pVoid)
  75. {
  76. HRESULT hres = S_OK;
  77. LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pScope;
  78. LPWSTR pScopeADsPath = OBJECT_NAME_FROM_SCOPE(pDsQueryScope);
  79. LPWSTR pScopeObjectClass = OBJECT_CLASS_FROM_SCOPE(pDsQueryScope);
  80. IADsPathname* pDsPathname = NULL;
  81. IDsDisplaySpecifier* pdds = NULL;
  82. BSTR bstrProvider = NULL;
  83. BSTR bstrLeaf = NULL;
  84. WCHAR szBuffer[MAX_PATH];
  85. USES_CONVERSION;
  86. TraceEnter(TRACE_SCOPES, "_ScopeProc");
  87. switch ( uMsg )
  88. {
  89. case CQSM_INITIALIZE:
  90. case CQSM_RELEASE:
  91. break;
  92. case CQSM_GETDISPLAYINFO:
  93. {
  94. LPCQSCOPEDISPLAYINFO pDisplayInfo = (LPCQSCOPEDISPLAYINFO)pVoid;
  95. LPTSTR pDirectoryName;
  96. TraceAssert(pDisplayInfo);
  97. TraceAssert(pDisplayInfo->pDisplayName);
  98. pDisplayInfo->iIndent = pDsQueryScope->iIndent;
  99. hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&pDsPathname);
  100. FailGracefully(hres, "Failed to get the IADsPathname interface");
  101. hres = pDsPathname->Set(pScopeADsPath, ADS_SETTYPE_FULL);
  102. FailGracefully(hres, "Failed to set the path of the name");
  103. hres = pDsPathname->Retrieve(ADS_FORMAT_PROVIDER, &bstrProvider);
  104. FailGracefully(hres, "Failed to get the provider");
  105. Trace(TEXT("Provider name is: %s"), W2T(bstrProvider));
  106. if ( !StrCmpW(bstrProvider, L"GC") )
  107. {
  108. TraceMsg("Provider is GC: so changing to Entire Directory");
  109. GetModuleFileName(GLOBAL_HINSTANCE, pDisplayInfo->pIconLocation, pDisplayInfo->cchIconLocation);
  110. pDisplayInfo->iIconResID = -IDI_GLOBALCATALOG;
  111. if ( SUCCEEDED(FormatDirectoryName(&pDirectoryName, GLOBAL_HINSTANCE, IDS_GLOBALCATALOG)) )
  112. {
  113. StrCpyN(pDisplayInfo->pDisplayName, pDirectoryName, pDisplayInfo->cchDisplayName);
  114. LocalFreeString(&pDirectoryName);
  115. }
  116. }
  117. else
  118. {
  119. TraceMsg("Non GC provider, so looking up icon and display name");
  120. //
  121. // get the leaf name for the object we want to display in the scope picker for the
  122. // DS.
  123. //
  124. pDsPathname->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  125. if ( SUCCEEDED(pDsPathname->Retrieve(ADS_FORMAT_LEAF, &bstrLeaf)) )
  126. {
  127. #if UNICODE
  128. StrCpyNW(pDisplayInfo->pDisplayName, bstrLeaf, pDisplayInfo->cchDisplayName);
  129. #else
  130. WideCharToMultiByte(CP_ACP, 0, bstrLeaf, -1,
  131. pDisplayInfo->pDisplayName, pDisplayInfo->cchDisplayName,
  132. 0, FALSE);
  133. #endif
  134. SysFreeString(bstrLeaf);
  135. }
  136. //
  137. // Now retrieve the display specifier information for the object.
  138. //
  139. hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (LPVOID*)&pdds);
  140. FailGracefully(hres, "Failed to get the IDsDisplaySpecifier object");
  141. #ifdef UNICODE
  142. pdds->GetIconLocation(pScopeObjectClass, DSGIF_GETDEFAULTICON,
  143. pDisplayInfo->pIconLocation, pDisplayInfo->cchIconLocation,
  144. &pDisplayInfo->iIconResID);
  145. #else
  146. pdds->GetIconLocation(pScopeObjectClass, DSGIF_GETDEFAULTICON,
  147. szBuffer, ARRAYSIZE(szBuffer),
  148. &pDisplayInfo->iIconResID);
  149. WideCharToMultiByte(CP_ACP, 0, szBuffer, -1,
  150. pDisplayInfo->pIconLocation, pDisplayInfo->cchIconLocation,
  151. 0, FALSE);
  152. #endif
  153. }
  154. break;
  155. }
  156. case CQSM_SCOPEEQUAL:
  157. {
  158. LPDSQUERYSCOPE pDsQueryScope2 = (LPDSQUERYSCOPE)pVoid;
  159. LPWSTR pScopeADsPath2 = OBJECT_NAME_FROM_SCOPE(pDsQueryScope2);
  160. Trace(TEXT("Comparing %s against %s"), W2T(pScopeADsPath), W2T(pScopeADsPath2));
  161. hres = StrCmpIW(pScopeADsPath, pScopeADsPath2) ? S_FALSE:S_OK;
  162. break;
  163. }
  164. default:
  165. hres = E_NOTIMPL;
  166. break;
  167. }
  168. exit_gracefully:
  169. SysFreeString(bstrProvider);
  170. DoRelease(pDsPathname);
  171. DoRelease(pdds);
  172. TraceLeaveResult(hres);
  173. }
  174. /*-----------------------------------------------------------------------------
  175. / AddScope
  176. / --------
  177. / Given an ADs path, get it converted to a scope block and then
  178. / call the add function to add it to the list of scopes we are going to be using.
  179. /
  180. / In:
  181. / ptd -> SCOPETHREADDATA structure
  182. / pDsQuery -> IQueryHandler interface to be AddRef'd
  183. i = index to insert the scope at
  184. / iIndent = horizontal indent
  185. / pPath -> ADS path to store as the scope
  186. / pObjectClass = object class to select
  187. / fSelect = if the scope should be selected
  188. /
  189. / Out:
  190. / HRESULT
  191. /----------------------------------------------------------------------------*/
  192. HRESULT AddScope(HWND hwndFrame, INT index, INT iIndent, LPWSTR pPath, LPWSTR pObjectClass, BOOL fSelect)
  193. {
  194. HRESULT hres;
  195. LPCQSCOPE pScope = NULL;
  196. USES_CONVERSION;
  197. TraceEnter(TRACE_SCOPES, "AddScope");
  198. Trace(TEXT("index %d, iIndent %d, fSelect %d"), index, iIndent, fSelect);
  199. Trace(TEXT("Object name: %s"), W2T(pPath));
  200. Trace(TEXT("Class: %s"), pObjectClass ? W2T(pObjectClass):TEXT("<none>"));
  201. hres = AllocScope(&pScope, iIndent, pPath, pObjectClass);
  202. FailGracefully(hres, "Failed to allocate DSQUERYSCOPE");
  203. if ( !SendMessage(hwndFrame, CQFWM_ADDSCOPE, (WPARAM)pScope, MAKELPARAM(fSelect, index)) )
  204. ExitGracefully(hres, E_FAIL, "Failed when sending ADDSCOPE message");
  205. hres = S_OK; // success
  206. exit_gracefully:
  207. if ( pScope )
  208. CoTaskMemFree(pScope);
  209. TraceLeaveResult(hres);
  210. }
  211. /*-----------------------------------------------------------------------------
  212. / AllocScope
  213. / ----------
  214. / Convert the given ADs path into a scope block that can be passed to the
  215. / common query interfaces.
  216. /
  217. / In:
  218. / iIndent = index to indent the scope by
  219. / ppScope = receives the newly allocated scope block
  220. / pPath -> name to package for the DS scope
  221. / pObjectClass -> object class of scope
  222. /
  223. / Out:
  224. / HRESULT
  225. /----------------------------------------------------------------------------*/
  226. HRESULT AllocScope(LPCQSCOPE* ppScope, INT iIndent, LPWSTR pPath, LPWSTR pObjectClass)
  227. {
  228. HRESULT hres;
  229. LPDSQUERYSCOPE pDsQueryScope = NULL;
  230. IADsPathname* pPathname = NULL;
  231. DWORD cb, offset;
  232. USES_CONVERSION;
  233. TraceEnter(TRACE_SCOPES, "AllocScope");
  234. Trace(TEXT("indent %d"), iIndent);
  235. Trace(TEXT("pPath: %s"), W2T(pPath));
  236. Trace(TEXT("pObjectClass: %s"), W2T(pObjectClass));
  237. // Allocate a new structure, note that the buffer for the ADs path is variable
  238. // size and lives at the end of the allocation.
  239. cb = SIZEOF(DSQUERYSCOPE) + StringByteSizeW(pPath) + StringByteSizeW(pObjectClass);;
  240. pDsQueryScope = (LPDSQUERYSCOPE)CoTaskMemAlloc(cb);
  241. TraceAssert(pDsQueryScope);
  242. if ( !pDsQueryScope )
  243. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate scope");
  244. pDsQueryScope->cq.cbStruct = cb;
  245. pDsQueryScope->cq.dwFlags = 0;
  246. pDsQueryScope->cq.pScopeProc = _ScopeProc;
  247. pDsQueryScope->cq.lParam = 0;
  248. pDsQueryScope->iIndent = iIndent;
  249. pDsQueryScope->dwOffsetADsPath = SIZEOF(DSQUERYSCOPE);
  250. pDsQueryScope->dwOffsetClass = 0;
  251. StringByteCopyW(pDsQueryScope, pDsQueryScope->dwOffsetADsPath, pPath);
  252. pDsQueryScope->dwOffsetClass = pDsQueryScope->dwOffsetADsPath + StringByteSizeW(pPath);
  253. StringByteCopyW(pDsQueryScope, pDsQueryScope->dwOffsetClass, pObjectClass);
  254. hres = S_OK; // success
  255. exit_gracefully:
  256. if ( ppScope )
  257. *ppScope = SUCCEEDED(hres) ? (LPCQSCOPE)pDsQueryScope:NULL;
  258. DoRelease(pPathname);
  259. TraceLeaveResult(hres);
  260. }
  261. /*-----------------------------------------------------------------------------
  262. / AddScopesThread
  263. / ---------------
  264. / Gather the scopes in the background and pass them to the
  265. / query window to allow it to populate the view scope controls.
  266. /
  267. / In:
  268. / pThreadParams -> structure that defines out thread information
  269. /
  270. / Out:
  271. / -
  272. /----------------------------------------------------------------------------*/
  273. // Walk the DOMAINDESC structures building ADSI paths and adding
  274. // them as search scopes to the scope list by calling AddScope
  275. // with the ADSI path stored in the domainDesc strucutre. If a domainDesc
  276. // entry has any children then recurse (increasing the indent). Otherwise
  277. // just continue through the piers.
  278. HRESULT _AddFromDomainTree(LPENUMSTATE pState, LPDOMAINDESC pDomainDesc, INT indent)
  279. {
  280. HRESULT hres;
  281. WCHAR szBuffer[MAX_PATH];
  282. DWORD dwIndex;
  283. BOOL fDefault = FALSE;
  284. USES_CONVERSION;
  285. TraceEnter(TRACE_SCOPES, "_AddFromDomainTree");
  286. while ( pDomainDesc )
  287. {
  288. //
  289. // include the server name in the path we are generating if we have one
  290. //
  291. StrCpyW(szBuffer, L"LDAP://");
  292. if ( pState->ptd->pServer )
  293. {
  294. StrCatW(szBuffer, pState->ptd->pServer);
  295. StrCatW(szBuffer, L"/");
  296. }
  297. StrCatW(szBuffer, pDomainDesc->pszNCName);
  298. Trace(TEXT("Scope is: %s"), W2T(szBuffer));
  299. //
  300. // now check to see if this is the default scope for the machine
  301. //
  302. if ( pState->pszDefaultDnsDomain )
  303. {
  304. if ( !StrCmpIW(pState->pszDefaultDnsDomain, pDomainDesc->pszName) )
  305. {
  306. TraceMsg("Default domain found in the domain list");
  307. fDefault = TRUE;
  308. }
  309. }
  310. //
  311. // add the scope, bumping the counters are required
  312. //
  313. hres = AddScope(pState->ptd->hwndFrame, pState->index, indent,
  314. szBuffer, pDomainDesc->pszObjectClass, fDefault);
  315. FailGracefully(hres, "Failed to add scope");
  316. pState->index++;
  317. pState->cScopes++; // bump the count before recursing
  318. if ( pDomainDesc->pdChildList )
  319. {
  320. hres = _AddFromDomainTree(pState, pDomainDesc->pdChildList, indent+1);
  321. FailGracefully(hres, "Failed to add children");
  322. }
  323. pDomainDesc = pDomainDesc->pdNextSibling;
  324. }
  325. hres = S_OK;
  326. exit_gracefully:
  327. TraceLeaveResult(hres);
  328. }
  329. DWORD WINAPI AddScopesThread(LPVOID pThreadParams)
  330. {
  331. HRESULT hres, hresCoInit;
  332. LPSCOPETHREADDATA ptd = (LPSCOPETHREADDATA)pThreadParams;
  333. IADs *pDsObject = NULL;
  334. IDsBrowseDomainTree* pDsDomains = NULL;
  335. BSTR bstrObjectClass = NULL;
  336. LPDOMAINTREE pDomainTree = NULL;
  337. ENUMSTATE enumState = { 0 };
  338. WCHAR szPath[MAX_PATH];
  339. WCHAR szDefaultDnsDomain[MAX_PATH];
  340. USES_CONVERSION;
  341. TraceEnter(TRACE_SCOPES, "AddScopesThread");
  342. hres = hresCoInit = CoInitialize(NULL);
  343. FailGracefully(hres, "Failed in CoInitialize");
  344. // Initialize ready to go and enumerate the scopes from the DS, this can be
  345. // quite a lengthy process therefore we live on a seperate thread.
  346. enumState.ptd = ptd;
  347. //enumState.index = 0;
  348. //enumState.cScopes = 0;
  349. //enumState.pszDefaultDnsDomain = NULL;
  350. // If the caller specified a scope we should be using then add it, if this
  351. // scope is already in the list we will end up select it anyway.
  352. if ( ptd->pDefaultScope )
  353. {
  354. Trace(TEXT("Adding default scope is: %s"), ptd->pDefaultScope);
  355. hres = ADsOpenObject(ptd->pDefaultScope, ptd->pUserName, ptd->pPassword,
  356. ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&pDsObject);
  357. if ( SUCCEEDED(hres) )
  358. {
  359. hres = pDsObject->get_Class(&bstrObjectClass);
  360. FailGracefully(hres, "Failed to get the object class");
  361. hres = AddScope(ptd->hwndFrame, 0, 0, ptd->pDefaultScope, bstrObjectClass, TRUE);
  362. FailGracefully(hres, "Failed to add the default scope during AddScopes");
  363. enumState.cScopes++;
  364. }
  365. }
  366. // Enumerate the GC using the GC: ADSI provider, this allows us to
  367. // have a single scope in the list, and avoids us having to pass
  368. // around the GC path to all and sundry.
  369. if ( SUCCEEDED(GetGlobalCatalogPath(ptd->pServer, szPath, ARRAYSIZE(szPath))) )
  370. {
  371. hres = AddScope(ptd->hwndFrame,
  372. enumState.index, 0,
  373. szPath, GC_OBJECTCLASS,
  374. FALSE);
  375. FailGracefully(hres, "Failed to add GC: too to the scope list");
  376. enumState.index++;
  377. enumState.cScopes++;
  378. }
  379. else if ( ptd->pDefaultScope )
  380. {
  381. //
  382. // get the domain the user has logged into, and use it to generate a default
  383. // scope that we can select in the list.
  384. //
  385. DWORD dwres;
  386. PDOMAIN_CONTROLLER_INFOW pdci = NULL;
  387. ULONG uFlags = DS_RETURN_DNS_NAME|DS_DIRECTORY_SERVICE_REQUIRED;
  388. INT cchDefaultDnsDomain;
  389. TraceMsg("No GC discovered, nor was a default scope, so setting default DNS domain accordingly");
  390. dwres = DsGetDcNameW(ptd->pServer, NULL, NULL, NULL, uFlags, &pdci);
  391. if ( ERROR_NO_SUCH_DOMAIN == dwres )
  392. {
  393. TraceMsg("Trying with rediscovery bit set");
  394. dwres = DsGetDcNameW(ptd->pServer, NULL, NULL, NULL, uFlags|DS_FORCE_REDISCOVERY, &pdci);
  395. }
  396. if ( (NO_ERROR == dwres) && pdci->DomainName && (pdci->Flags & DS_DNS_DOMAIN_FLAG) )
  397. {
  398. Trace(TEXT("Default domain name is: %s"), W2T(pdci->DomainName));
  399. StrCpyW(szDefaultDnsDomain, pdci->DnsForestName);
  400. cchDefaultDnsDomain = lstrlenW(szDefaultDnsDomain)-1;
  401. if ( cchDefaultDnsDomain && szDefaultDnsDomain[cchDefaultDnsDomain] == L'.' )
  402. {
  403. TraceMsg("Removing trailing . from the DNS name");
  404. szDefaultDnsDomain[cchDefaultDnsDomain] = L'\0';
  405. }
  406. enumState.pszDefaultDnsDomain = szDefaultDnsDomain;
  407. }
  408. NetApiBufferFree(pdci);
  409. }
  410. // Get the IDsBrowseDomainTree interface and ask it for the list of
  411. // trusted domains. Once we have that blob add them to the scope list,
  412. // indenting as requried to indicate the relationship. If we found a GC
  413. // then we must indent further, to indicate that all these are to be found
  414. // in the GC (as it encompases the entire org).
  415. hres = CoCreateInstance(CLSID_DsDomainTreeBrowser, NULL, CLSCTX_INPROC_SERVER,
  416. IID_IDsBrowseDomainTree, (LPVOID*)&pDsDomains);
  417. if ( SUCCEEDED(hres) )
  418. {
  419. hres = pDsDomains->SetComputer(ptd->pServer, ptd->pUserName, ptd->pPassword);
  420. FailGracefully(hres, "Failed when setting computer in the IDsBrowseDomainTree object");
  421. if ( SUCCEEDED(pDsDomains->GetDomains(&pDomainTree, DBDTF_RETURNFQDN)) )
  422. {
  423. Trace(TEXT("Domain count from GetDomains %d"), pDomainTree->dwCount);
  424. hres = _AddFromDomainTree(&enumState, &pDomainTree->aDomains[0], 0);
  425. FailGracefully(hres, "Failed to add from domain tree");
  426. }
  427. }
  428. hres = S_OK; // success
  429. exit_gracefully:
  430. // Release all our dangly bits
  431. DoRelease(pDsObject);
  432. SysFreeString(bstrObjectClass);
  433. if ( !enumState.cScopes )
  434. {
  435. // we have no scopes, therefore lets inform the user and post a close
  436. // message to the parent window so we can close it.
  437. FormatMsgBox(ptd->hwndFrame,
  438. GLOBAL_HINSTANCE, IDS_WINDOWTITLE, IDS_ERR_NOSCOPES,
  439. MB_OK|MB_ICONERROR);
  440. PostMessage(ptd->hwndFrame, WM_SYSCOMMAND, SC_CLOSE, 0L);
  441. }
  442. else
  443. {
  444. // tell tell the frame we ahve added all the scopes we will, that
  445. // way it can issue the query if the caller wants that.
  446. TraceMsg("Informing frame all scopes have been enumerated");
  447. SendMessage(ptd->hwndFrame, CQFWM_ALLSCOPESADDED, 0, 0);
  448. }
  449. if ( pDsDomains )
  450. {
  451. pDsDomains->FreeDomains(&pDomainTree);
  452. DoRelease(pDsDomains);
  453. }
  454. if ( ptd )
  455. {
  456. LocalFreeStringW(&ptd->pDefaultScope);
  457. LocalFreeStringW(&ptd->pServer);
  458. LocalFreeStringW(&ptd->pUserName);
  459. LocalFreeStringW(&ptd->pPassword);
  460. LocalFree((HLOCAL)ptd);
  461. }
  462. if ( SUCCEEDED(hresCoInit) )
  463. CoUninitialize();
  464. TraceLeave();
  465. DllRelease();
  466. ExitThread(0);
  467. return 0;
  468. }