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.

1283 lines
38 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000
  5. //
  6. // File: dsquery.cpp
  7. //
  8. // Contents: Defines the main function DSQUERY
  9. // command line utility
  10. //
  11. // History: 06-Sep-2000 hiteshr Created
  12. //
  13. //
  14. //--------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "cstrings.h"
  17. #include "usage.h"
  18. #include "querytable.h"
  19. #include "querybld.h"
  20. #include "dsquery.h"
  21. #include "query.h"
  22. #include "resource.h"
  23. #include "output.h"
  24. #include <dscmn.h>
  25. //
  26. //Structure Defined to Store Global Values at one place.
  27. //
  28. typedef struct _GlobalInfo
  29. {
  30. ADS_SCOPEENUM scope; //Scope of query
  31. DSQUERY_OUTPUT_FORMAT outputFormat; //Output Format
  32. }GLOBAL_INFO,*PGLOBAL_INFO;
  33. bool g_bQuiet = false;
  34. int g_iQueryLimit = 100;
  35. bool g_bDeafultLimit = true;
  36. //
  37. // Forward Function Declarations
  38. //
  39. HRESULT DoQueryValidation(PARG_RECORD pCommandArgs,
  40. PDSQueryObjectTableEntry pObjectEntry,
  41. PGLOBAL_INFO pcommon_info);
  42. HRESULT DoQuery(PARG_RECORD pCommandArgs,
  43. PDSQueryObjectTableEntry pObjectEntry,
  44. PGLOBAL_INFO pcommon_info);
  45. HRESULT GetAttributesToFetch(IN PGLOBAL_INFO pcommon_info,
  46. IN PARG_RECORD pCommandArgs,
  47. IN PDSQueryObjectTableEntry pObjectEntry,
  48. OUT LPWSTR **ppszAttributes,
  49. OUT DWORD * pCount);
  50. VOID FreeAttributesToFetch( IN LPWSTR *ppszAttributes,
  51. IN DWORD dwCount);
  52. HRESULT GetSearchRoot(IN IN PDSQueryObjectTableEntry pObjectEntry,
  53. IN PARG_RECORD pCommandArgs,
  54. IN CDSCmdBasePathsInfo& refBasePathsInfo,
  55. OUT CComBSTR& refsbstrDN,
  56. OUT BOOL *pbSearchAtForestRoot,
  57. OUT BOOL *pbSearchAtGC);
  58. HRESULT GetSearchObject(IN IN PDSQueryObjectTableEntry pObjectEntry,
  59. IN CDSCmdBasePathsInfo& refBasePathsInfo,
  60. IN PARG_RECORD pCommandArgs,
  61. IN CDSCmdCredentialObject& refCredentialObject,
  62. IN CComBSTR& refsbstrDN,
  63. IN BOOL bSearchAtForestRoot,
  64. IN BOOL bSearchAtGC,
  65. OUT CComPtr<IDirectorySearch>& refspSearchObject);
  66. //
  67. //Main Function
  68. //
  69. int __cdecl _tmain( VOID )
  70. {
  71. int argc = 0;
  72. LPTOKEN pToken = NULL;
  73. HRESULT hr = S_OK;
  74. PARG_RECORD pNewCommandArgs = 0;
  75. //
  76. // False loop
  77. //
  78. do
  79. {
  80. //
  81. // Initialize COM
  82. //
  83. hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  84. if (FAILED(hr))
  85. {
  86. DisplayErrorMessage(g_pszDSCommandName,
  87. NULL,
  88. hr);
  89. break;
  90. }
  91. //Get CommandLine Input
  92. hr = HRESULT_FROM_WIN32(GetCommandInput(&argc,&pToken));
  93. if (FAILED(hr))
  94. {
  95. DisplayErrorMessage(g_pszDSCommandName,
  96. NULL,
  97. hr);
  98. break;
  99. }
  100. if(argc == 1)
  101. {
  102. //
  103. // Display the error message and then break out of the false loop
  104. //
  105. DisplayMessage(USAGE_DSQUERY);
  106. hr = E_INVALIDARG;
  107. break;
  108. }
  109. //
  110. // Find which object table entry to use from
  111. // the second command line argument
  112. //
  113. PDSQueryObjectTableEntry pObjectEntry = NULL;
  114. UINT idx = 0;
  115. PWSTR pszObjectType = (pToken+1)->GetToken();
  116. while (true)
  117. {
  118. pObjectEntry = g_DSObjectTable[idx++];
  119. if (!pObjectEntry)
  120. {
  121. break;
  122. }
  123. if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
  124. {
  125. break;
  126. }
  127. }
  128. if (!pObjectEntry)
  129. {
  130. hr = E_INVALIDARG;
  131. if(argc == 2)
  132. {
  133. if(pToken[1].IsSwitch())
  134. {
  135. if(!wcscmp(pToken[1].GetToken(),L"/?") ||
  136. !wcscmp(pToken[1].GetToken(),L"/h") ||
  137. !wcscmp(pToken[1].GetToken(),L"-?") ||
  138. !wcscmp(pToken[1].GetToken(),L"-h"))
  139. hr = S_OK;
  140. }
  141. }
  142. //
  143. // Display the error message and then break out of the false loop
  144. //
  145. DisplayMessage(USAGE_DSQUERY);
  146. if (FAILED(hr))
  147. {
  148. DisplayErrorMessage(g_pszDSCommandName,
  149. NULL,
  150. hr);
  151. }
  152. break;
  153. }
  154. //
  155. // Now that we have the correct table entry, merge the command line table
  156. // for this object with the common commands
  157. //
  158. hr = MergeArgCommand(DSQUERY_COMMON_COMMANDS,
  159. pObjectEntry->pParserTable,
  160. &pNewCommandArgs);
  161. if (FAILED(hr))
  162. {
  163. DisplayErrorMessage(g_pszDSCommandName,
  164. NULL,
  165. hr);
  166. break;
  167. }
  168. //
  169. //Parse the Input
  170. //
  171. PARSE_ERROR Error;
  172. if(!ParseCmd(pNewCommandArgs,
  173. argc-1,
  174. pToken+1,
  175. pObjectEntry->nUsageID,
  176. &Error,
  177. TRUE))
  178. {
  179. if(Error.ErrorSource == ERROR_FROM_PARSER
  180. && Error.Error == PARSE_ERROR_HELP_SWITCH)
  181. {
  182. hr = S_OK;
  183. break;
  184. }
  185. hr = E_INVALIDARG;
  186. DisplayMessage(pObjectEntry->nUsageID);
  187. DisplayErrorMessage(g_pszDSCommandName,
  188. NULL,
  189. hr);
  190. break;
  191. }
  192. //
  193. // Check to see if we are doing debug spew
  194. //
  195. #ifdef DBG
  196. bool bDebugging = pNewCommandArgs[eCommDebug].bDefined &&
  197. pNewCommandArgs[eCommDebug].nValue;
  198. if (bDebugging)
  199. {
  200. ENABLE_DEBUG_OUTPUT(pNewCommandArgs[eCommDebug].nValue);
  201. }
  202. #else
  203. DISABLE_DEBUG_OUTPUT();
  204. #endif
  205. //
  206. // Set the global quiet flag
  207. //
  208. g_bQuiet = pNewCommandArgs[eCommQuiet].bDefined &&
  209. pNewCommandArgs[eCommQuiet].bValue;
  210. //
  211. //
  212. //
  213. if(pNewCommandArgs[eCommLimit].bDefined)
  214. {
  215. g_iQueryLimit = pNewCommandArgs[eCommLimit].nValue;
  216. g_bDeafultLimit = false;
  217. }
  218. GLOBAL_INFO common_info;
  219. common_info.scope = ADS_SCOPE_SUBTREE;
  220. common_info.outputFormat = DSQUERY_OUTPUT_DN;
  221. //
  222. // Do extra validation like switch dependency check etc.
  223. // Also collect Query Scope and Output format
  224. //
  225. hr = DoQueryValidation(pNewCommandArgs,
  226. pObjectEntry,
  227. &common_info);
  228. if (FAILED(hr))
  229. break;
  230. //
  231. // Command line parsing succeeded
  232. //
  233. hr = DoQuery(pNewCommandArgs,
  234. pObjectEntry,
  235. &common_info);
  236. if(FAILED(hr))
  237. break;
  238. } while (false); //False Loop
  239. //
  240. //Do the CleanUp
  241. //
  242. //
  243. // Free the memory associated with the command values
  244. //
  245. if(pNewCommandArgs)
  246. FreeCmd(pNewCommandArgs);
  247. //
  248. // Free the tokens
  249. //
  250. if (pToken)
  251. {
  252. delete[] pToken;
  253. pToken = 0;
  254. }
  255. //
  256. // Uninitialize COM
  257. //
  258. CoUninitialize();
  259. return hr;
  260. }
  261. //+--------------------------------------------------------------------------
  262. //
  263. // Function: DoQueryValidation
  264. //
  265. // Synopsis: Checks to be sure that command line switches that are mutually
  266. // exclusive are not both present and those that are dependent are
  267. // both presetn, and other validations which cannot be done by parser.
  268. //
  269. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  270. // to retrieve the values for each switch
  271. // [pObjectEntry - IN] : pointer to the object table entry for the
  272. // object type that will be queryied
  273. // [pcommon_info - OUT]: gets scope and output format info
  274. //
  275. // Returns: HRESULT : S_OK if everything succeeded
  276. // E_INVALIDARG if the object entry wasn't found
  277. //
  278. // History: 25-Sep-2000 hiteshr Created
  279. //
  280. //---------------------------------------------------------------------------
  281. HRESULT DoQueryValidation(IN PARG_RECORD pCommandArgs,
  282. IN PDSQueryObjectTableEntry pObjectEntry,
  283. OUT PGLOBAL_INFO pcommon_info)
  284. {
  285. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoQueryValidation, hr);
  286. if (!pCommandArgs || !pObjectEntry || !pcommon_info)
  287. {
  288. ASSERT(pCommandArgs);
  289. ASSERT(pObjectEntry);
  290. ASSERT(pcommon_info);
  291. hr = E_INVALIDARG;
  292. return hr;
  293. }
  294. //
  295. //Validate OutputFormat for "dsquery objectType"
  296. //
  297. if(_wcsicmp(pObjectEntry->pszCommandLineObjectType,g_pszStar))
  298. {
  299. DEBUG_OUTPUT(MINIMAL_LOGGING, L"dsquery <objectType> processing will be performed");
  300. if(pCommandArgs[eCommOutputFormat].bDefined &&
  301. pCommandArgs[eCommOutputFormat].strValue)
  302. {
  303. //
  304. //ppValidOutput contains the validoutput type for a
  305. //given object type
  306. //
  307. ASSERT(pObjectEntry->ppValidOutput);
  308. BOOL bMatch = FALSE;
  309. for(UINT i = 0; i < pObjectEntry->dwOutputCount; ++i)
  310. {
  311. if(_wcsicmp(pCommandArgs[eCommOutputFormat].strValue,
  312. pObjectEntry->ppValidOutput[i]->pszOutputFormat) == 0 )
  313. {
  314. bMatch = TRUE;
  315. pcommon_info->outputFormat = pObjectEntry->ppValidOutput[i]->outputFormat;
  316. break;
  317. }
  318. }
  319. if(!bMatch)
  320. {
  321. DisplayMessage(pObjectEntry->nUsageID);
  322. hr = E_INVALIDARG;
  323. DisplayErrorMessage(g_pszDSCommandName,
  324. NULL,
  325. hr);
  326. return hr;
  327. }
  328. }
  329. //
  330. //default output format is DN
  331. //
  332. else
  333. pcommon_info->outputFormat = DSQUERY_OUTPUT_DN;
  334. }
  335. else
  336. {
  337. //
  338. //-o is invalid switch form dsquery *, but since its
  339. //common for all other objects its kept in common table
  340. //and we do the special casing for dsquery *
  341. //
  342. if(pCommandArgs[eCommOutputFormat].bDefined &&
  343. pCommandArgs[eCommOutputFormat].strValue)
  344. {
  345. DisplayMessage(pObjectEntry->nUsageID);
  346. hr = E_INVALIDARG;
  347. DisplayErrorMessage(g_pszDSCommandName,
  348. NULL,
  349. hr);
  350. return hr;
  351. }
  352. DEBUG_OUTPUT(MINIMAL_LOGGING, L"dsquery * processing will be performed");
  353. if(pCommandArgs[eStarAttrsOnly].bDefined)
  354. pcommon_info->outputFormat = DSQUERY_OUTPUT_ATTRONLY;
  355. else
  356. pcommon_info->outputFormat = DSQUERY_OUTPUT_ATTR;
  357. }
  358. //
  359. //Validate Scope string.
  360. //default scope is subtree.
  361. //
  362. pcommon_info->scope = ADS_SCOPE_SUBTREE;
  363. if(pObjectEntry->nScopeID != -1)
  364. {
  365. if( pCommandArgs[pObjectEntry->nScopeID].bDefined &&
  366. pCommandArgs[pObjectEntry->nScopeID].strValue )
  367. {
  368. LPCWSTR pszScope = pCommandArgs[pObjectEntry->nScopeID].strValue;
  369. if( _wcsicmp(pszScope,g_pszSubTree) == 0 )
  370. {
  371. DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = subtree");
  372. pcommon_info->scope = ADS_SCOPE_SUBTREE;
  373. }
  374. else if( _wcsicmp(pszScope,g_pszOneLevel) == 0 )
  375. {
  376. DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = onelevel");
  377. pcommon_info->scope = ADS_SCOPE_ONELEVEL;
  378. }
  379. else if( _wcsicmp(pszScope,g_pszBase) == 0 )
  380. {
  381. DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = base");
  382. pcommon_info->scope = ADS_SCOPE_BASE;
  383. }
  384. else
  385. {
  386. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Unknown scope = %s", pszScope);
  387. DisplayMessage(pObjectEntry->nUsageID);
  388. hr = E_INVALIDARG;
  389. DisplayErrorMessage(g_pszDSCommandName,
  390. NULL,
  391. hr);
  392. return hr;
  393. }
  394. }
  395. }
  396. //
  397. //Limit must be 0 or greater
  398. //
  399. if(pCommandArgs[eCommLimit].bDefined)
  400. {
  401. if(pCommandArgs[eCommLimit].nValue < 0)
  402. {
  403. DisplayMessage(pObjectEntry->nUsageID);
  404. hr = E_INVALIDARG;
  405. DisplayErrorMessage(g_pszDSCommandName,
  406. NULL,
  407. hr);
  408. return hr;
  409. }
  410. }
  411. //
  412. //Forestwide Search implies the -GC switch so define it if it isn't already
  413. //
  414. if(pCommandArgs[eCommStartNode].bDefined &&
  415. pCommandArgs[eCommStartNode].strValue )
  416. {
  417. if(_wcsicmp(pCommandArgs[eCommStartNode].strValue,g_pszForestRoot) == 0)
  418. {
  419. if(!(pCommandArgs[eCommGC].bDefined &&
  420. pCommandArgs[eCommGC].bValue))
  421. {
  422. pCommandArgs[eCommGC].bDefined = TRUE;
  423. pCommandArgs[eCommGC].bValue = TRUE;
  424. }
  425. }
  426. }
  427. //
  428. //For dsquery server, if none of the -domain, -forest, -site is
  429. //specified, then define -domain as its default
  430. //
  431. if(!_wcsicmp(pObjectEntry->pszCommandLineObjectType,g_pszServer))
  432. {
  433. //
  434. //Value is assigned in DoQuery function
  435. //
  436. if(!pCommandArgs[eServerDomain].bDefined &&
  437. !pCommandArgs[eServerForest].bDefined &&
  438. !pCommandArgs[eServerSite].bDefined)
  439. {
  440. pCommandArgs[eServerDomain].bDefined = TRUE;
  441. }
  442. }
  443. return hr;
  444. }
  445. //+--------------------------------------------------------------------------
  446. //
  447. // Function: DoQuery
  448. //
  449. // Synopsis: Does the query
  450. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  451. // to retrieve the values for each switch
  452. // [pObjectEntry - IN] : pointer to the object table entry for the
  453. // object type that will be modified
  454. // [pcommon_info - IN] : scope and outputformat info
  455. //
  456. // Returns: HRESULT : S_OK if everything succeeded
  457. // E_INVALIDARG if the object entry wasn't found
  458. // Anything else is a failure code from an ADSI call
  459. //
  460. // History: 25-Sep-2000 hiteshr Created
  461. //
  462. //---------------------------------------------------------------------------
  463. HRESULT DoQuery(PARG_RECORD pCommandArgs,
  464. PDSQueryObjectTableEntry pObjectEntry,
  465. PGLOBAL_INFO pcommon_info)
  466. {
  467. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoQuery, hr);
  468. if (!pCommandArgs || !pObjectEntry || !pcommon_info)
  469. {
  470. ASSERT(pCommandArgs);
  471. ASSERT(pObjectEntry);
  472. ASSERT(pcommon_info);
  473. hr = E_INVALIDARG;
  474. return hr;
  475. }
  476. CDSCmdCredentialObject credentialObject;
  477. if (pCommandArgs[eCommUserName].bDefined)
  478. {
  479. credentialObject.SetUsername(pCommandArgs[eCommUserName].strValue);
  480. credentialObject.SetUsingCredentials(true);
  481. }
  482. if (pCommandArgs[eCommPassword].bDefined)
  483. {
  484. credentialObject.SetPassword(pCommandArgs[eCommPassword].strValue);
  485. credentialObject.SetUsingCredentials(true);
  486. }
  487. //
  488. // Initialize the base paths info from the command line args
  489. //
  490. CDSCmdBasePathsInfo basePathsInfo;
  491. if (pCommandArgs[eCommServer].bDefined)
  492. {
  493. hr = basePathsInfo.InitializeFromName(credentialObject,
  494. pCommandArgs[eCommServer].strValue,
  495. true);
  496. }
  497. else if (pCommandArgs[eCommDomain].bDefined)
  498. {
  499. hr = basePathsInfo.InitializeFromName(credentialObject,
  500. pCommandArgs[eCommDomain].strValue,
  501. false);
  502. }
  503. else
  504. {
  505. hr = basePathsInfo.InitializeFromName(credentialObject, NULL, false);
  506. }
  507. if (FAILED(hr))
  508. {
  509. DisplayErrorMessage(g_pszDSCommandName,
  510. NULL,
  511. hr);
  512. return hr;
  513. }
  514. //
  515. //Check if to search GC and get the search root path
  516. //
  517. BOOL bSearchAtGC = FALSE;
  518. BOOL bSearchAtForestRoot = FALSE;
  519. CComBSTR sbstrObjectDN;
  520. hr = GetSearchRoot(pObjectEntry,
  521. pCommandArgs,
  522. basePathsInfo,
  523. sbstrObjectDN,
  524. &bSearchAtForestRoot,
  525. &bSearchAtGC);
  526. if (FAILED(hr))
  527. {
  528. DisplayErrorMessage(g_pszDSCommandName,
  529. NULL,
  530. hr);
  531. return hr;
  532. }
  533. DEBUG_OUTPUT(MINIMAL_LOGGING, L"start node = %s", sbstrObjectDN);
  534. //
  535. //Build The Filter For Query
  536. //
  537. PVOID pParam = NULL;
  538. if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSubnet) == 0)
  539. {
  540. CComBSTR strSubSiteSuffix;
  541. GetSiteContainerPath(basePathsInfo, strSubSiteSuffix);
  542. pParam = (PVOID)&strSubSiteSuffix;
  543. }
  544. CComBSTR strLDAPFilter;
  545. hr = BuildQueryFilter(pCommandArgs,
  546. pObjectEntry,
  547. pParam,
  548. strLDAPFilter);
  549. if (FAILED(hr))
  550. {
  551. DisplayErrorMessage(g_pszDSCommandName,
  552. NULL,
  553. hr);
  554. return hr;
  555. }
  556. //
  557. //Create The IDirectorySearchObject
  558. //
  559. CComPtr<IDirectorySearch> spSearchObject;
  560. hr = GetSearchObject(pObjectEntry,
  561. basePathsInfo,
  562. pCommandArgs,
  563. credentialObject,
  564. sbstrObjectDN,
  565. bSearchAtForestRoot,
  566. bSearchAtGC,
  567. spSearchObject);
  568. if (FAILED(hr))
  569. {
  570. DisplayErrorMessage(g_pszDSCommandName,
  571. NULL,
  572. hr);
  573. return hr;
  574. }
  575. //
  576. //Get the arributes to fetch
  577. //
  578. LPWSTR *ppszAttributes = NULL;
  579. DWORD dwCountAttr = 0;
  580. hr = GetAttributesToFetch(pcommon_info,
  581. pCommandArgs,
  582. pObjectEntry,
  583. &ppszAttributes,
  584. &dwCountAttr);
  585. if (FAILED(hr))
  586. {
  587. DisplayErrorMessage(g_pszDSCommandName,
  588. NULL,
  589. hr);
  590. return hr;
  591. }
  592. //
  593. //Lets Query Now
  594. //
  595. CDSSearch searchObject;
  596. hr = searchObject.Init(spSearchObject);
  597. if (FAILED(hr))
  598. {
  599. DEBUG_OUTPUT(MINIMAL_LOGGING,
  600. L"Initializing search object failed: hr = 0x%x",
  601. hr);
  602. FreeAttributesToFetch(ppszAttributes, dwCountAttr);
  603. DisplayErrorMessage(g_pszDSCommandName,
  604. NULL,
  605. hr);
  606. return hr;
  607. }
  608. searchObject.SetFilterString(strLDAPFilter);
  609. searchObject.SetSearchScope(pcommon_info->scope);
  610. searchObject.SetAttributeList(ppszAttributes,dwCountAttr?dwCountAttr:-1);
  611. hr = searchObject.DoQuery();
  612. if(FAILED(hr))
  613. {
  614. DEBUG_OUTPUT(MINIMAL_LOGGING, L"DoQuery failed hr = 0x%x", hr);
  615. FreeAttributesToFetch(ppszAttributes,dwCountAttr);
  616. DisplayErrorMessage(g_pszDSCommandName,
  617. NULL,
  618. hr);
  619. return hr;
  620. }
  621. //
  622. //Find out the display format for dsquery *
  623. //It can be either List or Table
  624. //
  625. BOOL bListFormat = TRUE;
  626. if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR)
  627. {
  628. //
  629. //If all attributes are to be displayed, only List Format is valid
  630. //If attributes to fetch are specified at commandline, Table is default format.
  631. if(dwCountAttr &&
  632. !pCommandArgs[eStarList].bDefined)
  633. bListFormat = FALSE;
  634. }
  635. bool bUseStandardOutput = true;
  636. if (pCommandArgs[eCommObjectType].bDefined &&
  637. _wcsicmp(pCommandArgs[eCommObjectType].strValue, g_pszServer) == 0)
  638. {
  639. //
  640. // "dsquery server" requires additional processing if either the
  641. // -isgc or the -hasfsmo switch is specified
  642. //
  643. if ((pCommandArgs[eServerIsGC].bDefined && pCommandArgs[eServerIsGC].bValue) ||
  644. (pCommandArgs[eServerHasFSMO].bDefined && pCommandArgs[eServerHasFSMO].strValue)||
  645. (pCommandArgs[eServerDomain].bDefined && pCommandArgs[eServerDomain].strValue))
  646. {
  647. bUseStandardOutput = false;
  648. hr = DsQueryServerOutput(pcommon_info->outputFormat,
  649. ppszAttributes,
  650. dwCountAttr,
  651. searchObject,
  652. credentialObject,
  653. basePathsInfo,
  654. pCommandArgs);
  655. if (FAILED(hr))
  656. {
  657. DisplayErrorMessage(g_pszDSCommandName,
  658. NULL,
  659. hr);
  660. }
  661. }
  662. }
  663. if (bUseStandardOutput)
  664. {
  665. //
  666. //Output the result of search
  667. //
  668. hr = DsQueryOutput(pcommon_info->outputFormat,
  669. ppszAttributes,
  670. dwCountAttr,
  671. &searchObject,
  672. bListFormat);
  673. if (FAILED(hr))
  674. {
  675. DisplayErrorMessage(g_pszDSCommandName,
  676. NULL,
  677. hr);
  678. }
  679. }
  680. FreeAttributesToFetch(ppszAttributes,dwCountAttr);
  681. return hr;
  682. }
  683. //+--------------------------------------------------------------------------
  684. //
  685. // Function: GetAttributesToFetch
  686. //
  687. // Synopsis: Make an array of attributes to fetch.
  688. // Arguments: [pcommon_info - IN] : outputformat and scope info
  689. // [ppszAttributes - OUT] : array of attributes to fetch
  690. // [pCount - OUT] : count of attributes in array
  691. //
  692. // Returns: HRESULT : S_OK if everything succeeded
  693. // E_INVALIDARG if the object entry wasn't found
  694. // Anything else is a failure code from an ADSI call
  695. //
  696. // History: 25-Sep-2000 hiteshr Created
  697. //
  698. //---------------------------------------------------------------------------
  699. HRESULT GetAttributesToFetch(IN PGLOBAL_INFO pcommon_info,
  700. IN PARG_RECORD pCommandArgs,
  701. IN PDSQueryObjectTableEntry pObjectEntry,
  702. OUT LPWSTR **ppszAttributes,
  703. OUT DWORD * pCount)
  704. {
  705. ENTER_FUNCTION_HR(MINIMAL_LOGGING, GetAttributesToFetch, hr);
  706. if(!pcommon_info || !pCommandArgs || !pObjectEntry)
  707. {
  708. ASSERT(pcommon_info);
  709. ASSERT(pCommandArgs);
  710. ASSERT(pObjectEntry);
  711. hr = E_INVALIDARG;
  712. return hr;
  713. }
  714. if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR ||
  715. pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTRONLY)
  716. {
  717. if(pCommandArgs[eStarAttr].bDefined)
  718. {
  719. //
  720. //If input is "*", fetch all attributes
  721. //
  722. if(wcscmp(pCommandArgs[eStarAttr].strValue,L"*") == 0 )
  723. {
  724. *ppszAttributes = NULL;
  725. *pCount = 0;
  726. return hr;
  727. }
  728. LPWSTR *ppszTemp = NULL;
  729. UINT argc = 0;
  730. ParseNullSeparatedString(pCommandArgs[eStarAttr].strValue,
  731. &ppszTemp,
  732. &argc);
  733. LPWSTR *ppszAttr = (LPWSTR *)LocalAlloc(LPTR,argc*sizeof(LPCTSTR));
  734. if(!ppszAttr)
  735. {
  736. hr = E_OUTOFMEMORY;
  737. return hr;
  738. }
  739. for(UINT i = 0; i < argc; ++i)
  740. {
  741. if(FAILED(LocalCopyString(ppszAttr+i, ppszTemp[i])))
  742. {
  743. LocalFree(ppszAttr);
  744. hr = E_OUTOFMEMORY;
  745. return hr;
  746. }
  747. }
  748. *ppszAttributes = ppszAttr;
  749. *pCount = argc;
  750. if(ppszTemp)
  751. LocalFree(ppszTemp);
  752. hr = S_OK;
  753. return hr;
  754. }
  755. }
  756. LPCWSTR pszAttr = NULL;
  757. if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR)
  758. {
  759. //
  760. //If eStarAttr is not defined, Fetch only DN
  761. pcommon_info->outputFormat = DSQUERY_OUTPUT_DN;
  762. pszAttr = g_szAttrDistinguishedName;
  763. }
  764. else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTRONLY)
  765. pszAttr = g_szAttrDistinguishedName;
  766. else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_DN)
  767. pszAttr = g_szAttrDistinguishedName;
  768. else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_UPN)
  769. pszAttr = g_szAttrUserPrincipalName;
  770. else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_SAMID)
  771. pszAttr = g_szAttrSamAccountName;
  772. else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_RDN)
  773. pszAttr = g_szAttrRDN;
  774. //
  775. // Always include the DN in the search results as well. It is quite useful.
  776. //
  777. size_t entries = 2;
  778. if (pCommandArgs[eServerDomain].bDefined &&
  779. pCommandArgs[eServerDomain].strValue)
  780. {
  781. //
  782. // If the scope is DOMAIN then add an addition space for the serverReference
  783. ++entries;
  784. }
  785. LPWSTR *ppszAttr = (LPWSTR *)LocalAlloc(LPTR,sizeof(LPWSTR) * entries);
  786. if(!ppszAttr)
  787. {
  788. hr = E_OUTOFMEMORY;
  789. return hr;
  790. }
  791. ZeroMemory(ppszAttr, sizeof(LPWSTR) * entries);
  792. if(FAILED(LocalCopyString(ppszAttr,pszAttr)))
  793. {
  794. LocalFree(ppszAttr);
  795. hr = E_OUTOFMEMORY;
  796. return hr;
  797. }
  798. //
  799. // Always include the DN in the search results as well. It is quite useful.
  800. //
  801. if (FAILED(LocalCopyString(&(ppszAttr[1]), g_szAttrDistinguishedName)))
  802. {
  803. LocalFree(ppszAttr);
  804. hr = E_OUTOFMEMORY;
  805. return hr;
  806. }
  807. if (pCommandArgs[eServerDomain].bDefined &&
  808. pCommandArgs[eServerDomain].strValue)
  809. {
  810. ASSERT(entries >= 3);
  811. if (FAILED(LocalCopyString(&(ppszAttr[2]), g_szAttrServerReference)))
  812. {
  813. LocalFree(ppszAttr);
  814. hr = E_OUTOFMEMORY;
  815. return hr;
  816. }
  817. }
  818. *ppszAttributes = ppszAttr;
  819. *pCount = static_cast<DWORD>(entries);
  820. return hr;
  821. }
  822. //+--------------------------------------------------------------------------
  823. //
  824. // Function: FreeAttributesToFetch
  825. //
  826. // Synopsis: Function to free memory allocated by GetAttributesToFetch
  827. // Arguments: [dwszAttributes - in] : array of attributes to fetch
  828. // [dwCount - in] : count of attributes in array
  829. //
  830. // Returns: HRESULT : S_OK if everything succeeded
  831. // E_INVALIDARG if the object entry wasn't found
  832. // Anything else is a failure code from an ADSI call
  833. //
  834. // History: 25-Sep-2000 hiteshr Created
  835. //
  836. //---------------------------------------------------------------------------
  837. VOID FreeAttributesToFetch( IN LPWSTR *ppszAttributes,
  838. IN DWORD dwCount)
  839. {
  840. while(dwCount)
  841. {
  842. LocalFree(ppszAttributes[--dwCount]);
  843. }
  844. LocalFree(ppszAttributes);
  845. }
  846. //+--------------------------------------------------------------------------
  847. //
  848. // Function: GetSearchRoot
  849. //
  850. // Synopsis: Builds the path to the root of the search as determined by
  851. // the parameters passed in from the command line.
  852. //
  853. // Arguments: [pObjectEntry - IN] : pointer to the object table entry for the
  854. // object type that will be modified
  855. // [pCommandArgs IN] : the table of the command line input
  856. // [refBasePathsInfo IN] : reference to the base paths info
  857. // [refsbstrDN OUT] : reference to a CComBSTR that will
  858. // receive the DN at which to start
  859. // the search
  860. // [pbSearchAtForestRoot] :Set to true is startnode is equal to
  861. // forestroot
  862. //
  863. // Returns: HRESULT
  864. //
  865. // History: 24-April-2001 hiteshr Created
  866. //
  867. //---------------------------------------------------------------------------
  868. HRESULT GetSearchRoot(IN IN PDSQueryObjectTableEntry pObjectEntry,
  869. IN PARG_RECORD pCommandArgs,
  870. IN CDSCmdBasePathsInfo& refBasePathsInfo,
  871. OUT CComBSTR& refsbstrDN,
  872. OUT BOOL *pbSearchAtForestRoot,
  873. OUT BOOL *pbSearchAtGC)
  874. {
  875. if(!pCommandArgs ||
  876. !pObjectEntry ||
  877. !pbSearchAtForestRoot ||
  878. !pbSearchAtGC)
  879. {
  880. return E_POINTER;
  881. }
  882. PWSTR pszInputDN = NULL;
  883. if(pCommandArgs[eCommGC].bDefined &&
  884. pCommandArgs[eCommGC].bValue)
  885. {
  886. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Searching the GC");
  887. *pbSearchAtGC = TRUE;
  888. }
  889. //
  890. //Get the starting node
  891. //
  892. if(pCommandArgs[eCommStartNode].bDefined &&
  893. pCommandArgs[eCommStartNode].strValue )
  894. {
  895. pszInputDN = pCommandArgs[eCommStartNode].strValue;
  896. if(_wcsicmp(pszInputDN,g_pszDomainRoot) == 0)
  897. {
  898. refsbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  899. }
  900. else if(_wcsicmp(pszInputDN,g_pszForestRoot) == 0)
  901. {
  902. *pbSearchAtForestRoot = TRUE;
  903. }
  904. else
  905. {
  906. //
  907. //DN is entered
  908. //
  909. refsbstrDN = pszInputDN;
  910. }
  911. }
  912. else
  913. {
  914. if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszServer) == 0)
  915. {
  916. if (pCommandArgs[eServerDomain].bDefined &&
  917. !pCommandArgs[eServerDomain].strValue)
  918. {
  919. PWSTR pszName = 0;
  920. PWSTR pszDomainName = refBasePathsInfo.GetDefaultNamingContext();
  921. HRESULT hr = CrackName(pszDomainName,
  922. &pszName,
  923. GET_DNS_DOMAIN_NAME,
  924. NULL);
  925. if (FAILED(hr))
  926. {
  927. DEBUG_OUTPUT(LEVEL3_LOGGING,
  928. L"Failed to crack the DN into a domain name: hr = 0x%x",
  929. hr);
  930. DisplayErrorMessage(g_pszDSCommandName,
  931. NULL,
  932. hr);
  933. return hr;
  934. }
  935. pCommandArgs[eServerDomain].strValue = pszName;
  936. }
  937. //
  938. // Get the base path that corresponds with the scope
  939. //
  940. GetServerSearchRoot(pCommandArgs,
  941. refBasePathsInfo,
  942. refsbstrDN);
  943. }
  944. else if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSite) == 0)
  945. {
  946. //
  947. // Scope is the configuration container
  948. //
  949. refsbstrDN = refBasePathsInfo.GetConfigurationNamingContext();
  950. }
  951. else if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSubnet) == 0)
  952. {
  953. //
  954. // Get the base path that corresponds with the scope
  955. //
  956. GetSubnetSearchRoot(refBasePathsInfo,
  957. refsbstrDN);
  958. }
  959. else
  960. {
  961. //
  962. //default is Domain DN
  963. //
  964. refsbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  965. }
  966. }
  967. return S_OK;
  968. }
  969. HRESULT GetSearchObject(IN IN PDSQueryObjectTableEntry pObjectEntry,
  970. IN CDSCmdBasePathsInfo& refBasePathsInfo,
  971. IN PARG_RECORD pCommandArgs,
  972. IN CDSCmdCredentialObject& refCredentialObject,
  973. IN CComBSTR& refsbstrDN,
  974. IN BOOL bSearchAtForestRoot,
  975. IN BOOL bSearchAtGC,
  976. OUT CComPtr<IDirectorySearch>& refspSearchObject)
  977. {
  978. HRESULT hr = S_OK;
  979. if(!pObjectEntry || !pCommandArgs)
  980. return E_POINTER;
  981. if(!bSearchAtForestRoot)
  982. {
  983. CComBSTR sbstrObjectPath;
  984. bool bBindToServer = true;
  985. if(bSearchAtGC)
  986. {
  987. //
  988. //Change the provider in sbstrObjectPath from LDAP to GC
  989. //
  990. CComPtr<IADsPathname> spPathNameObject;
  991. hr = CoCreateInstance(CLSID_Pathname,
  992. NULL,
  993. CLSCTX_INPROC_SERVER,
  994. IID_IADsPathname,
  995. (LPVOID*)&spPathNameObject);
  996. if (FAILED(hr))
  997. {
  998. DisplayErrorMessage(g_pszDSCommandName,
  999. NULL,
  1000. hr);
  1001. return hr;
  1002. }
  1003. //Set Provider to GC
  1004. hr = spPathNameObject->Set(L"GC", ADS_SETTYPE_PROVIDER);
  1005. ASSERT(SUCCEEDED(hr));
  1006. //Set the DN
  1007. hr = spPathNameObject->Set(refsbstrDN, ADS_SETTYPE_DN);
  1008. ASSERT(SUCCEEDED(hr));
  1009. hr = spPathNameObject->Retrieve(ADS_FORMAT_X500_NO_SERVER, &sbstrObjectPath);
  1010. ASSERT(SUCCEEDED(hr));
  1011. //
  1012. //No server name in path
  1013. //
  1014. bBindToServer = false;
  1015. }
  1016. else
  1017. {
  1018. //
  1019. // Convert the DN to an ADSI path
  1020. //
  1021. refBasePathsInfo.ComposePathFromDN(refsbstrDN, sbstrObjectPath);
  1022. if((_wcsicmp(pObjectEntry->pszObjectClass, g_pszUser) == 0 &&
  1023. pCommandArgs[eUserInactive].bDefined) ||
  1024. (_wcsicmp(pObjectEntry->pszObjectClass, g_pszComputer) == 0 &&
  1025. pCommandArgs[eComputerInactive].bDefined))
  1026. {
  1027. INT nDomainBehaviorVersion = 0;
  1028. CComPtr<IADs> spDomain;
  1029. CComBSTR sbstrBasePath;
  1030. refBasePathsInfo.ComposePathFromDN(refBasePathsInfo.GetDefaultNamingContext(),
  1031. sbstrBasePath);
  1032. hr = DSCmdOpenObject(refCredentialObject,
  1033. sbstrBasePath,
  1034. IID_IADs,
  1035. (void**)&spDomain,
  1036. bBindToServer);
  1037. if (FAILED(hr))
  1038. {
  1039. nDomainBehaviorVersion = 0;
  1040. }
  1041. CComVariant varVer;
  1042. hr = spDomain->GetInfo();
  1043. if(SUCCEEDED(hr))
  1044. {
  1045. CComBSTR bstrVer = L"msDS-Behavior-Version";
  1046. hr = spDomain->Get(bstrVer, &varVer);
  1047. }
  1048. if (FAILED(hr))
  1049. {
  1050. nDomainBehaviorVersion = 0;
  1051. }
  1052. else
  1053. {
  1054. ASSERT(varVer.vt == VT_I4);
  1055. nDomainBehaviorVersion = static_cast<UINT>(varVer.lVal);
  1056. }
  1057. if(nDomainBehaviorVersion == 0)
  1058. {
  1059. DisplayMessage(pObjectEntry->nUsageID);
  1060. DisplayErrorMessage(g_pszDSCommandName,
  1061. NULL,
  1062. hr,
  1063. IDS_FILTER_LAST_LOGON_VERSION);
  1064. hr = E_INVALIDARG;
  1065. return hr;
  1066. }
  1067. }
  1068. }
  1069. hr = DSCmdOpenObject(refCredentialObject,
  1070. sbstrObjectPath,
  1071. IID_IDirectorySearch,
  1072. (void**)&refspSearchObject,
  1073. bBindToServer);
  1074. if (FAILED(hr))
  1075. {
  1076. DisplayErrorMessage(g_pszDSCommandName,
  1077. NULL,
  1078. hr);
  1079. return hr;
  1080. }
  1081. }
  1082. else
  1083. {
  1084. //
  1085. //BIND to GC to search entire forest
  1086. //
  1087. CComPtr<IADsContainer> spContainer;
  1088. hr = DSCmdOpenObject(refCredentialObject,
  1089. L"GC:",
  1090. IID_IADsContainer,
  1091. (void**)&spContainer,
  1092. false);
  1093. if (FAILED(hr))
  1094. {
  1095. DisplayErrorMessage(g_pszDSCommandName,
  1096. NULL,
  1097. hr);
  1098. return hr;
  1099. }
  1100. //
  1101. // Get an enumeration interface for the GC container to enumerate the
  1102. // contents. The "real" GC is the only child of the GC container.
  1103. //
  1104. CComPtr<IEnumVARIANT> spEnum = NULL;
  1105. hr = ADsBuildEnumerator(spContainer, &spEnum);
  1106. if (FAILED(hr))
  1107. {
  1108. DisplayErrorMessage(g_pszDSCommandName,
  1109. NULL,
  1110. hr);
  1111. return hr;
  1112. }
  1113. //
  1114. // Now enumerate. There's only one child of the GC: object.
  1115. //
  1116. VARIANT var;
  1117. ULONG lFetch;
  1118. hr = spEnum->Next(1, &var, &lFetch);
  1119. if (FAILED(hr))
  1120. {
  1121. DisplayErrorMessage(g_pszDSCommandName,
  1122. NULL,
  1123. hr);
  1124. return hr;
  1125. }
  1126. // Get the IDirectorySearch pointer.
  1127. if (lFetch == 1)
  1128. {
  1129. CComPtr<IDispatch> spDisp = V_DISPATCH(&var);
  1130. hr = spDisp->QueryInterface( IID_IDirectorySearch, (void**)&refspSearchObject);
  1131. if (FAILED(hr))
  1132. {
  1133. DisplayErrorMessage(g_pszDSCommandName,
  1134. NULL,
  1135. hr);
  1136. return hr;
  1137. }
  1138. }
  1139. else
  1140. {
  1141. //
  1142. //Need to put a better message here
  1143. //
  1144. DisplayErrorMessage(g_pszDSCommandName,
  1145. NULL,
  1146. hr);
  1147. return E_FAIL;
  1148. }
  1149. }
  1150. return hr;
  1151. }