1284 lines
38 KiB
C++
1284 lines
38 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 2000
|
|
//
|
|
// File: dsquery.cpp
|
|
//
|
|
// Contents: Defines the main function DSQUERY
|
|
// command line utility
|
|
//
|
|
// History: 06-Sep-2000 hiteshr Created
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#include "cstrings.h"
|
|
#include "usage.h"
|
|
#include "querytable.h"
|
|
#include "querybld.h"
|
|
#include "dsquery.h"
|
|
#include "query.h"
|
|
#include "resource.h"
|
|
#include "output.h"
|
|
#include <dscmn.h>
|
|
|
|
//
|
|
//Structure Defined to Store Global Values at one place.
|
|
//
|
|
typedef struct _GlobalInfo
|
|
{
|
|
ADS_SCOPEENUM scope; //Scope of query
|
|
DSQUERY_OUTPUT_FORMAT outputFormat; //Output Format
|
|
}GLOBAL_INFO,*PGLOBAL_INFO;
|
|
|
|
bool g_bQuiet = false;
|
|
int g_iQueryLimit = 100;
|
|
bool g_bDeafultLimit = true;
|
|
|
|
//
|
|
// Forward Function Declarations
|
|
//
|
|
HRESULT DoQueryValidation(PARG_RECORD pCommandArgs,
|
|
PDSQueryObjectTableEntry pObjectEntry,
|
|
PGLOBAL_INFO pcommon_info);
|
|
|
|
HRESULT DoQuery(PARG_RECORD pCommandArgs,
|
|
PDSQueryObjectTableEntry pObjectEntry,
|
|
PGLOBAL_INFO pcommon_info);
|
|
|
|
HRESULT GetAttributesToFetch(IN PGLOBAL_INFO pcommon_info,
|
|
IN PARG_RECORD pCommandArgs,
|
|
IN PDSQueryObjectTableEntry pObjectEntry,
|
|
OUT LPWSTR **ppszAttributes,
|
|
OUT DWORD * pCount);
|
|
VOID FreeAttributesToFetch( IN LPWSTR *ppszAttributes,
|
|
IN DWORD dwCount);
|
|
|
|
HRESULT GetSearchRoot(IN IN PDSQueryObjectTableEntry pObjectEntry,
|
|
IN PARG_RECORD pCommandArgs,
|
|
IN CDSCmdBasePathsInfo& refBasePathsInfo,
|
|
OUT CComBSTR& refsbstrDN,
|
|
OUT BOOL *pbSearchAtForestRoot,
|
|
OUT BOOL *pbSearchAtGC);
|
|
|
|
HRESULT GetSearchObject(IN IN PDSQueryObjectTableEntry pObjectEntry,
|
|
IN CDSCmdBasePathsInfo& refBasePathsInfo,
|
|
IN PARG_RECORD pCommandArgs,
|
|
IN CDSCmdCredentialObject& refCredentialObject,
|
|
IN CComBSTR& refsbstrDN,
|
|
IN BOOL bSearchAtForestRoot,
|
|
IN BOOL bSearchAtGC,
|
|
OUT CComPtr<IDirectorySearch>& refspSearchObject);
|
|
|
|
|
|
|
|
//
|
|
//Main Function
|
|
//
|
|
int __cdecl _tmain( VOID )
|
|
{
|
|
|
|
int argc = 0;
|
|
LPTOKEN pToken = NULL;
|
|
HRESULT hr = S_OK;
|
|
PARG_RECORD pNewCommandArgs = 0;
|
|
|
|
//
|
|
// False loop
|
|
//
|
|
do
|
|
{
|
|
//
|
|
// Initialize COM
|
|
//
|
|
hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
break;
|
|
}
|
|
|
|
//Get CommandLine Input
|
|
hr = HRESULT_FROM_WIN32(GetCommandInput(&argc,&pToken));
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
break;
|
|
}
|
|
|
|
if(argc == 1)
|
|
{
|
|
//
|
|
// Display the error message and then break out of the false loop
|
|
//
|
|
DisplayMessage(USAGE_DSQUERY);
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find which object table entry to use from
|
|
// the second command line argument
|
|
//
|
|
PDSQueryObjectTableEntry pObjectEntry = NULL;
|
|
UINT idx = 0;
|
|
PWSTR pszObjectType = (pToken+1)->GetToken();
|
|
while (true)
|
|
{
|
|
pObjectEntry = g_DSObjectTable[idx++];
|
|
if (!pObjectEntry)
|
|
{
|
|
break;
|
|
}
|
|
if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pObjectEntry)
|
|
{
|
|
|
|
hr = E_INVALIDARG;
|
|
if(argc == 2)
|
|
{
|
|
if(pToken[1].IsSwitch())
|
|
{
|
|
if(!wcscmp(pToken[1].GetToken(),L"/?") ||
|
|
!wcscmp(pToken[1].GetToken(),L"/h") ||
|
|
!wcscmp(pToken[1].GetToken(),L"-?") ||
|
|
!wcscmp(pToken[1].GetToken(),L"-h"))
|
|
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
//
|
|
// Display the error message and then break out of the false loop
|
|
//
|
|
DisplayMessage(USAGE_DSQUERY);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now that we have the correct table entry, merge the command line table
|
|
// for this object with the common commands
|
|
//
|
|
hr = MergeArgCommand(DSQUERY_COMMON_COMMANDS,
|
|
pObjectEntry->pParserTable,
|
|
&pNewCommandArgs);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
//Parse the Input
|
|
//
|
|
PARSE_ERROR Error;
|
|
if(!ParseCmd(pNewCommandArgs,
|
|
argc-1,
|
|
pToken+1,
|
|
pObjectEntry->nUsageID,
|
|
&Error,
|
|
TRUE))
|
|
{
|
|
if(Error.ErrorSource == ERROR_FROM_PARSER
|
|
&& Error.Error == PARSE_ERROR_HELP_SWITCH)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
hr = E_INVALIDARG;
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check to see if we are doing debug spew
|
|
//
|
|
#ifdef DBG
|
|
bool bDebugging = pNewCommandArgs[eCommDebug].bDefined &&
|
|
pNewCommandArgs[eCommDebug].nValue;
|
|
if (bDebugging)
|
|
{
|
|
ENABLE_DEBUG_OUTPUT(pNewCommandArgs[eCommDebug].nValue);
|
|
}
|
|
#else
|
|
DISABLE_DEBUG_OUTPUT();
|
|
#endif
|
|
|
|
//
|
|
// Set the global quiet flag
|
|
//
|
|
g_bQuiet = pNewCommandArgs[eCommQuiet].bDefined &&
|
|
pNewCommandArgs[eCommQuiet].bValue;
|
|
|
|
//
|
|
//
|
|
//
|
|
if(pNewCommandArgs[eCommLimit].bDefined)
|
|
{
|
|
g_iQueryLimit = pNewCommandArgs[eCommLimit].nValue;
|
|
g_bDeafultLimit = false;
|
|
}
|
|
|
|
|
|
GLOBAL_INFO common_info;
|
|
common_info.scope = ADS_SCOPE_SUBTREE;
|
|
common_info.outputFormat = DSQUERY_OUTPUT_DN;
|
|
|
|
//
|
|
// Do extra validation like switch dependency check etc.
|
|
// Also collect Query Scope and Output format
|
|
//
|
|
hr = DoQueryValidation(pNewCommandArgs,
|
|
pObjectEntry,
|
|
&common_info);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
//
|
|
// Command line parsing succeeded
|
|
//
|
|
hr = DoQuery(pNewCommandArgs,
|
|
pObjectEntry,
|
|
&common_info);
|
|
if(FAILED(hr))
|
|
break;
|
|
|
|
|
|
} while (false); //False Loop
|
|
|
|
//
|
|
//Do the CleanUp
|
|
//
|
|
|
|
//
|
|
// Free the memory associated with the command values
|
|
//
|
|
if(pNewCommandArgs)
|
|
FreeCmd(pNewCommandArgs);
|
|
|
|
//
|
|
// Free the tokens
|
|
//
|
|
if (pToken)
|
|
{
|
|
delete[] pToken;
|
|
pToken = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Uninitialize COM
|
|
//
|
|
CoUninitialize();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: DoQueryValidation
|
|
//
|
|
// Synopsis: Checks to be sure that command line switches that are mutually
|
|
// exclusive are not both present and those that are dependent are
|
|
// both presetn, and other validations which cannot be done by parser.
|
|
//
|
|
// Arguments: [pCommandArgs - IN] : the command line argument structure used
|
|
// to retrieve the values for each switch
|
|
// [pObjectEntry - IN] : pointer to the object table entry for the
|
|
// object type that will be queryied
|
|
// [pcommon_info - OUT]: gets scope and output format info
|
|
//
|
|
// Returns: HRESULT : S_OK if everything succeeded
|
|
// E_INVALIDARG if the object entry wasn't found
|
|
//
|
|
// History: 25-Sep-2000 hiteshr Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT DoQueryValidation(IN PARG_RECORD pCommandArgs,
|
|
IN PDSQueryObjectTableEntry pObjectEntry,
|
|
OUT PGLOBAL_INFO pcommon_info)
|
|
{
|
|
ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoQueryValidation, hr);
|
|
|
|
if (!pCommandArgs || !pObjectEntry || !pcommon_info)
|
|
{
|
|
ASSERT(pCommandArgs);
|
|
ASSERT(pObjectEntry);
|
|
ASSERT(pcommon_info);
|
|
hr = E_INVALIDARG;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
//Validate OutputFormat for "dsquery objectType"
|
|
//
|
|
if(_wcsicmp(pObjectEntry->pszCommandLineObjectType,g_pszStar))
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"dsquery <objectType> processing will be performed");
|
|
|
|
if(pCommandArgs[eCommOutputFormat].bDefined &&
|
|
pCommandArgs[eCommOutputFormat].strValue)
|
|
{
|
|
//
|
|
//ppValidOutput contains the validoutput type for a
|
|
//given object type
|
|
//
|
|
ASSERT(pObjectEntry->ppValidOutput);
|
|
BOOL bMatch = FALSE;
|
|
for(UINT i = 0; i < pObjectEntry->dwOutputCount; ++i)
|
|
{
|
|
if(_wcsicmp(pCommandArgs[eCommOutputFormat].strValue,
|
|
pObjectEntry->ppValidOutput[i]->pszOutputFormat) == 0 )
|
|
{
|
|
bMatch = TRUE;
|
|
pcommon_info->outputFormat = pObjectEntry->ppValidOutput[i]->outputFormat;
|
|
break;
|
|
}
|
|
}
|
|
if(!bMatch)
|
|
{
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_INVALIDARG;
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
//
|
|
//default output format is DN
|
|
//
|
|
else
|
|
pcommon_info->outputFormat = DSQUERY_OUTPUT_DN;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//-o is invalid switch form dsquery *, but since its
|
|
//common for all other objects its kept in common table
|
|
//and we do the special casing for dsquery *
|
|
//
|
|
if(pCommandArgs[eCommOutputFormat].bDefined &&
|
|
pCommandArgs[eCommOutputFormat].strValue)
|
|
{
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_INVALIDARG;
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"dsquery * processing will be performed");
|
|
if(pCommandArgs[eStarAttrsOnly].bDefined)
|
|
pcommon_info->outputFormat = DSQUERY_OUTPUT_ATTRONLY;
|
|
else
|
|
pcommon_info->outputFormat = DSQUERY_OUTPUT_ATTR;
|
|
}
|
|
|
|
//
|
|
//Validate Scope string.
|
|
//default scope is subtree.
|
|
//
|
|
pcommon_info->scope = ADS_SCOPE_SUBTREE;
|
|
if(pObjectEntry->nScopeID != -1)
|
|
{
|
|
if( pCommandArgs[pObjectEntry->nScopeID].bDefined &&
|
|
pCommandArgs[pObjectEntry->nScopeID].strValue )
|
|
{
|
|
LPCWSTR pszScope = pCommandArgs[pObjectEntry->nScopeID].strValue;
|
|
if( _wcsicmp(pszScope,g_pszSubTree) == 0 )
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = subtree");
|
|
pcommon_info->scope = ADS_SCOPE_SUBTREE;
|
|
}
|
|
else if( _wcsicmp(pszScope,g_pszOneLevel) == 0 )
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = onelevel");
|
|
pcommon_info->scope = ADS_SCOPE_ONELEVEL;
|
|
}
|
|
else if( _wcsicmp(pszScope,g_pszBase) == 0 )
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"scope = base");
|
|
pcommon_info->scope = ADS_SCOPE_BASE;
|
|
}
|
|
else
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Unknown scope = %s", pszScope);
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_INVALIDARG;
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//Limit must be 0 or greater
|
|
//
|
|
if(pCommandArgs[eCommLimit].bDefined)
|
|
{
|
|
if(pCommandArgs[eCommLimit].nValue < 0)
|
|
{
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_INVALIDARG;
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//Forestwide Search implies the -GC switch so define it if it isn't already
|
|
//
|
|
if(pCommandArgs[eCommStartNode].bDefined &&
|
|
pCommandArgs[eCommStartNode].strValue )
|
|
{
|
|
if(_wcsicmp(pCommandArgs[eCommStartNode].strValue,g_pszForestRoot) == 0)
|
|
{
|
|
if(!(pCommandArgs[eCommGC].bDefined &&
|
|
pCommandArgs[eCommGC].bValue))
|
|
{
|
|
pCommandArgs[eCommGC].bDefined = TRUE;
|
|
pCommandArgs[eCommGC].bValue = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//For dsquery server, if none of the -domain, -forest, -site is
|
|
//specified, then define -domain as its default
|
|
//
|
|
if(!_wcsicmp(pObjectEntry->pszCommandLineObjectType,g_pszServer))
|
|
{
|
|
//
|
|
//Value is assigned in DoQuery function
|
|
//
|
|
if(!pCommandArgs[eServerDomain].bDefined &&
|
|
!pCommandArgs[eServerForest].bDefined &&
|
|
!pCommandArgs[eServerSite].bDefined)
|
|
{
|
|
pCommandArgs[eServerDomain].bDefined = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: DoQuery
|
|
//
|
|
// Synopsis: Does the query
|
|
// Arguments: [pCommandArgs - IN] : the command line argument structure used
|
|
// to retrieve the values for each switch
|
|
// [pObjectEntry - IN] : pointer to the object table entry for the
|
|
// object type that will be modified
|
|
// [pcommon_info - IN] : scope and outputformat info
|
|
//
|
|
// Returns: HRESULT : S_OK if everything succeeded
|
|
// E_INVALIDARG if the object entry wasn't found
|
|
// Anything else is a failure code from an ADSI call
|
|
//
|
|
// History: 25-Sep-2000 hiteshr Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT DoQuery(PARG_RECORD pCommandArgs,
|
|
PDSQueryObjectTableEntry pObjectEntry,
|
|
PGLOBAL_INFO pcommon_info)
|
|
{
|
|
ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoQuery, hr);
|
|
|
|
if (!pCommandArgs || !pObjectEntry || !pcommon_info)
|
|
{
|
|
ASSERT(pCommandArgs);
|
|
ASSERT(pObjectEntry);
|
|
ASSERT(pcommon_info);
|
|
hr = E_INVALIDARG;
|
|
return hr;
|
|
}
|
|
|
|
CDSCmdCredentialObject credentialObject;
|
|
if (pCommandArgs[eCommUserName].bDefined)
|
|
{
|
|
credentialObject.SetUsername(pCommandArgs[eCommUserName].strValue);
|
|
credentialObject.SetUsingCredentials(true);
|
|
}
|
|
|
|
if (pCommandArgs[eCommPassword].bDefined)
|
|
{
|
|
credentialObject.SetPassword(pCommandArgs[eCommPassword].strValue);
|
|
credentialObject.SetUsingCredentials(true);
|
|
}
|
|
|
|
//
|
|
// Initialize the base paths info from the command line args
|
|
//
|
|
CDSCmdBasePathsInfo basePathsInfo;
|
|
if (pCommandArgs[eCommServer].bDefined)
|
|
{
|
|
hr = basePathsInfo.InitializeFromName(credentialObject,
|
|
pCommandArgs[eCommServer].strValue,
|
|
true);
|
|
}
|
|
else if (pCommandArgs[eCommDomain].bDefined)
|
|
{
|
|
hr = basePathsInfo.InitializeFromName(credentialObject,
|
|
pCommandArgs[eCommDomain].strValue,
|
|
false);
|
|
}
|
|
else
|
|
{
|
|
hr = basePathsInfo.InitializeFromName(credentialObject, NULL, false);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
//Check if to search GC and get the search root path
|
|
//
|
|
BOOL bSearchAtGC = FALSE;
|
|
BOOL bSearchAtForestRoot = FALSE;
|
|
CComBSTR sbstrObjectDN;
|
|
|
|
hr = GetSearchRoot(pObjectEntry,
|
|
pCommandArgs,
|
|
basePathsInfo,
|
|
sbstrObjectDN,
|
|
&bSearchAtForestRoot,
|
|
&bSearchAtGC);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"start node = %s", sbstrObjectDN);
|
|
|
|
//
|
|
//Build The Filter For Query
|
|
//
|
|
|
|
PVOID pParam = NULL;
|
|
if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSubnet) == 0)
|
|
{
|
|
CComBSTR strSubSiteSuffix;
|
|
GetSiteContainerPath(basePathsInfo, strSubSiteSuffix);
|
|
pParam = (PVOID)&strSubSiteSuffix;
|
|
}
|
|
|
|
CComBSTR strLDAPFilter;
|
|
hr = BuildQueryFilter(pCommandArgs,
|
|
pObjectEntry,
|
|
pParam,
|
|
strLDAPFilter);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//Create The IDirectorySearchObject
|
|
//
|
|
CComPtr<IDirectorySearch> spSearchObject;
|
|
hr = GetSearchObject(pObjectEntry,
|
|
basePathsInfo,
|
|
pCommandArgs,
|
|
credentialObject,
|
|
sbstrObjectDN,
|
|
bSearchAtForestRoot,
|
|
bSearchAtGC,
|
|
spSearchObject);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//Get the arributes to fetch
|
|
//
|
|
LPWSTR *ppszAttributes = NULL;
|
|
DWORD dwCountAttr = 0;
|
|
hr = GetAttributesToFetch(pcommon_info,
|
|
pCommandArgs,
|
|
pObjectEntry,
|
|
&ppszAttributes,
|
|
&dwCountAttr);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
//Lets Query Now
|
|
//
|
|
CDSSearch searchObject;
|
|
hr = searchObject.Init(spSearchObject);
|
|
if (FAILED(hr))
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING,
|
|
L"Initializing search object failed: hr = 0x%x",
|
|
hr);
|
|
FreeAttributesToFetch(ppszAttributes, dwCountAttr);
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
searchObject.SetFilterString(strLDAPFilter);
|
|
searchObject.SetSearchScope(pcommon_info->scope);
|
|
searchObject.SetAttributeList(ppszAttributes,dwCountAttr?dwCountAttr:-1);
|
|
hr = searchObject.DoQuery();
|
|
if(FAILED(hr))
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"DoQuery failed hr = 0x%x", hr);
|
|
FreeAttributesToFetch(ppszAttributes,dwCountAttr);
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
//
|
|
//Find out the display format for dsquery *
|
|
//It can be either List or Table
|
|
//
|
|
BOOL bListFormat = TRUE;
|
|
if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR)
|
|
{
|
|
//
|
|
//If all attributes are to be displayed, only List Format is valid
|
|
//If attributes to fetch are specified at commandline, Table is default format.
|
|
if(dwCountAttr &&
|
|
!pCommandArgs[eStarList].bDefined)
|
|
bListFormat = FALSE;
|
|
}
|
|
|
|
bool bUseStandardOutput = true;
|
|
if (pCommandArgs[eCommObjectType].bDefined &&
|
|
_wcsicmp(pCommandArgs[eCommObjectType].strValue, g_pszServer) == 0)
|
|
{
|
|
//
|
|
// "dsquery server" requires additional processing if either the
|
|
// -isgc or the -hasfsmo switch is specified
|
|
//
|
|
if ((pCommandArgs[eServerIsGC].bDefined && pCommandArgs[eServerIsGC].bValue) ||
|
|
(pCommandArgs[eServerHasFSMO].bDefined && pCommandArgs[eServerHasFSMO].strValue)||
|
|
(pCommandArgs[eServerDomain].bDefined && pCommandArgs[eServerDomain].strValue))
|
|
{
|
|
bUseStandardOutput = false;
|
|
hr = DsQueryServerOutput(pcommon_info->outputFormat,
|
|
ppszAttributes,
|
|
dwCountAttr,
|
|
searchObject,
|
|
credentialObject,
|
|
basePathsInfo,
|
|
pCommandArgs);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (bUseStandardOutput)
|
|
{
|
|
//
|
|
//Output the result of search
|
|
//
|
|
hr = DsQueryOutput(pcommon_info->outputFormat,
|
|
ppszAttributes,
|
|
dwCountAttr,
|
|
&searchObject,
|
|
bListFormat);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
}
|
|
}
|
|
|
|
FreeAttributesToFetch(ppszAttributes,dwCountAttr);
|
|
return hr;
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: GetAttributesToFetch
|
|
//
|
|
// Synopsis: Make an array of attributes to fetch.
|
|
// Arguments: [pcommon_info - IN] : outputformat and scope info
|
|
// [ppszAttributes - OUT] : array of attributes to fetch
|
|
// [pCount - OUT] : count of attributes in array
|
|
//
|
|
// Returns: HRESULT : S_OK if everything succeeded
|
|
// E_INVALIDARG if the object entry wasn't found
|
|
// Anything else is a failure code from an ADSI call
|
|
//
|
|
// History: 25-Sep-2000 hiteshr Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT GetAttributesToFetch(IN PGLOBAL_INFO pcommon_info,
|
|
IN PARG_RECORD pCommandArgs,
|
|
IN PDSQueryObjectTableEntry pObjectEntry,
|
|
OUT LPWSTR **ppszAttributes,
|
|
OUT DWORD * pCount)
|
|
{
|
|
ENTER_FUNCTION_HR(MINIMAL_LOGGING, GetAttributesToFetch, hr);
|
|
|
|
if(!pcommon_info || !pCommandArgs || !pObjectEntry)
|
|
{
|
|
ASSERT(pcommon_info);
|
|
ASSERT(pCommandArgs);
|
|
ASSERT(pObjectEntry);
|
|
hr = E_INVALIDARG;
|
|
return hr;
|
|
}
|
|
|
|
if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR ||
|
|
pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTRONLY)
|
|
{
|
|
if(pCommandArgs[eStarAttr].bDefined)
|
|
{
|
|
//
|
|
//If input is "*", fetch all attributes
|
|
//
|
|
if(wcscmp(pCommandArgs[eStarAttr].strValue,L"*") == 0 )
|
|
{
|
|
*ppszAttributes = NULL;
|
|
*pCount = 0;
|
|
return hr;
|
|
}
|
|
|
|
|
|
LPWSTR *ppszTemp = NULL;
|
|
UINT argc = 0;
|
|
ParseNullSeparatedString(pCommandArgs[eStarAttr].strValue,
|
|
&ppszTemp,
|
|
&argc);
|
|
|
|
|
|
LPWSTR *ppszAttr = (LPWSTR *)LocalAlloc(LPTR,argc*sizeof(LPCTSTR));
|
|
if(!ppszAttr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
for(UINT i = 0; i < argc; ++i)
|
|
{
|
|
if(FAILED(LocalCopyString(ppszAttr+i, ppszTemp[i])))
|
|
{
|
|
LocalFree(ppszAttr);
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
}
|
|
*ppszAttributes = ppszAttr;
|
|
*pCount = argc;
|
|
if(ppszTemp)
|
|
LocalFree(ppszTemp);
|
|
hr = S_OK;
|
|
return hr;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
LPCWSTR pszAttr = NULL;
|
|
if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTR)
|
|
{
|
|
//
|
|
//If eStarAttr is not defined, Fetch only DN
|
|
pcommon_info->outputFormat = DSQUERY_OUTPUT_DN;
|
|
pszAttr = g_szAttrDistinguishedName;
|
|
}
|
|
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_ATTRONLY)
|
|
pszAttr = g_szAttrDistinguishedName;
|
|
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_DN)
|
|
pszAttr = g_szAttrDistinguishedName;
|
|
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_UPN)
|
|
pszAttr = g_szAttrUserPrincipalName;
|
|
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_SAMID)
|
|
pszAttr = g_szAttrSamAccountName;
|
|
else if(pcommon_info->outputFormat == DSQUERY_OUTPUT_RDN)
|
|
pszAttr = g_szAttrRDN;
|
|
|
|
//
|
|
// Always include the DN in the search results as well. It is quite useful.
|
|
//
|
|
size_t entries = 2;
|
|
if (pCommandArgs[eServerDomain].bDefined &&
|
|
pCommandArgs[eServerDomain].strValue)
|
|
{
|
|
//
|
|
// If the scope is DOMAIN then add an addition space for the serverReference
|
|
++entries;
|
|
}
|
|
|
|
LPWSTR *ppszAttr = (LPWSTR *)LocalAlloc(LPTR,sizeof(LPWSTR) * entries);
|
|
if(!ppszAttr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
|
|
ZeroMemory(ppszAttr, sizeof(LPWSTR) * entries);
|
|
|
|
if(FAILED(LocalCopyString(ppszAttr,pszAttr)))
|
|
{
|
|
LocalFree(ppszAttr);
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Always include the DN in the search results as well. It is quite useful.
|
|
//
|
|
if (FAILED(LocalCopyString(&(ppszAttr[1]), g_szAttrDistinguishedName)))
|
|
{
|
|
LocalFree(ppszAttr);
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
|
|
if (pCommandArgs[eServerDomain].bDefined &&
|
|
pCommandArgs[eServerDomain].strValue)
|
|
{
|
|
ASSERT(entries >= 3);
|
|
if (FAILED(LocalCopyString(&(ppszAttr[2]), g_szAttrServerReference)))
|
|
{
|
|
LocalFree(ppszAttr);
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
*ppszAttributes = ppszAttr;
|
|
*pCount = static_cast<DWORD>(entries);
|
|
return hr;
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: FreeAttributesToFetch
|
|
//
|
|
// Synopsis: Function to free memory allocated by GetAttributesToFetch
|
|
// Arguments: [dwszAttributes - in] : array of attributes to fetch
|
|
// [dwCount - in] : count of attributes in array
|
|
//
|
|
// Returns: HRESULT : S_OK if everything succeeded
|
|
// E_INVALIDARG if the object entry wasn't found
|
|
// Anything else is a failure code from an ADSI call
|
|
//
|
|
// History: 25-Sep-2000 hiteshr Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
VOID FreeAttributesToFetch( IN LPWSTR *ppszAttributes,
|
|
IN DWORD dwCount)
|
|
{
|
|
while(dwCount)
|
|
{
|
|
LocalFree(ppszAttributes[--dwCount]);
|
|
}
|
|
LocalFree(ppszAttributes);
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: GetSearchRoot
|
|
//
|
|
// Synopsis: Builds the path to the root of the search as determined by
|
|
// the parameters passed in from the command line.
|
|
//
|
|
// Arguments: [pObjectEntry - IN] : pointer to the object table entry for the
|
|
// object type that will be modified
|
|
// [pCommandArgs IN] : the table of the command line input
|
|
// [refBasePathsInfo IN] : reference to the base paths info
|
|
// [refsbstrDN OUT] : reference to a CComBSTR that will
|
|
// receive the DN at which to start
|
|
// the search
|
|
// [pbSearchAtForestRoot] :Set to true is startnode is equal to
|
|
// forestroot
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// History: 24-April-2001 hiteshr Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT GetSearchRoot(IN IN PDSQueryObjectTableEntry pObjectEntry,
|
|
IN PARG_RECORD pCommandArgs,
|
|
IN CDSCmdBasePathsInfo& refBasePathsInfo,
|
|
OUT CComBSTR& refsbstrDN,
|
|
OUT BOOL *pbSearchAtForestRoot,
|
|
OUT BOOL *pbSearchAtGC)
|
|
{
|
|
if(!pCommandArgs ||
|
|
!pObjectEntry ||
|
|
!pbSearchAtForestRoot ||
|
|
!pbSearchAtGC)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
PWSTR pszInputDN = NULL;
|
|
|
|
if(pCommandArgs[eCommGC].bDefined &&
|
|
pCommandArgs[eCommGC].bValue)
|
|
{
|
|
DEBUG_OUTPUT(LEVEL5_LOGGING, L"Searching the GC");
|
|
*pbSearchAtGC = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
//Get the starting node
|
|
//
|
|
if(pCommandArgs[eCommStartNode].bDefined &&
|
|
pCommandArgs[eCommStartNode].strValue )
|
|
{
|
|
pszInputDN = pCommandArgs[eCommStartNode].strValue;
|
|
if(_wcsicmp(pszInputDN,g_pszDomainRoot) == 0)
|
|
{
|
|
refsbstrDN = refBasePathsInfo.GetDefaultNamingContext();
|
|
}
|
|
else if(_wcsicmp(pszInputDN,g_pszForestRoot) == 0)
|
|
{
|
|
*pbSearchAtForestRoot = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//DN is entered
|
|
//
|
|
refsbstrDN = pszInputDN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszServer) == 0)
|
|
{
|
|
if (pCommandArgs[eServerDomain].bDefined &&
|
|
!pCommandArgs[eServerDomain].strValue)
|
|
{
|
|
PWSTR pszName = 0;
|
|
PWSTR pszDomainName = refBasePathsInfo.GetDefaultNamingContext();
|
|
HRESULT hr = CrackName(pszDomainName,
|
|
&pszName,
|
|
GET_DNS_DOMAIN_NAME,
|
|
NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DEBUG_OUTPUT(LEVEL3_LOGGING,
|
|
L"Failed to crack the DN into a domain name: hr = 0x%x",
|
|
hr);
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
|
|
}
|
|
pCommandArgs[eServerDomain].strValue = pszName;
|
|
}
|
|
|
|
//
|
|
// Get the base path that corresponds with the scope
|
|
//
|
|
GetServerSearchRoot(pCommandArgs,
|
|
refBasePathsInfo,
|
|
refsbstrDN);
|
|
}
|
|
else if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSite) == 0)
|
|
{
|
|
//
|
|
// Scope is the configuration container
|
|
//
|
|
refsbstrDN = refBasePathsInfo.GetConfigurationNamingContext();
|
|
}
|
|
else if (_wcsicmp(pObjectEntry->pszObjectClass, g_pszSubnet) == 0)
|
|
{
|
|
//
|
|
// Get the base path that corresponds with the scope
|
|
//
|
|
GetSubnetSearchRoot(refBasePathsInfo,
|
|
refsbstrDN);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//default is Domain DN
|
|
//
|
|
refsbstrDN = refBasePathsInfo.GetDefaultNamingContext();
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT GetSearchObject(IN IN PDSQueryObjectTableEntry pObjectEntry,
|
|
IN CDSCmdBasePathsInfo& refBasePathsInfo,
|
|
IN PARG_RECORD pCommandArgs,
|
|
IN CDSCmdCredentialObject& refCredentialObject,
|
|
IN CComBSTR& refsbstrDN,
|
|
IN BOOL bSearchAtForestRoot,
|
|
IN BOOL bSearchAtGC,
|
|
OUT CComPtr<IDirectorySearch>& refspSearchObject)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if(!pObjectEntry || !pCommandArgs)
|
|
return E_POINTER;
|
|
|
|
if(!bSearchAtForestRoot)
|
|
{
|
|
CComBSTR sbstrObjectPath;
|
|
bool bBindToServer = true;
|
|
if(bSearchAtGC)
|
|
{
|
|
//
|
|
//Change the provider in sbstrObjectPath from LDAP to GC
|
|
//
|
|
CComPtr<IADsPathname> spPathNameObject;
|
|
hr = CoCreateInstance(CLSID_Pathname,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IADsPathname,
|
|
(LPVOID*)&spPathNameObject);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
//Set Provider to GC
|
|
hr = spPathNameObject->Set(L"GC", ADS_SETTYPE_PROVIDER);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
//Set the DN
|
|
hr = spPathNameObject->Set(refsbstrDN, ADS_SETTYPE_DN);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr = spPathNameObject->Retrieve(ADS_FORMAT_X500_NO_SERVER, &sbstrObjectPath);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
//
|
|
//No server name in path
|
|
//
|
|
bBindToServer = false;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Convert the DN to an ADSI path
|
|
//
|
|
refBasePathsInfo.ComposePathFromDN(refsbstrDN, sbstrObjectPath);
|
|
|
|
if((_wcsicmp(pObjectEntry->pszObjectClass, g_pszUser) == 0 &&
|
|
pCommandArgs[eUserInactive].bDefined) ||
|
|
(_wcsicmp(pObjectEntry->pszObjectClass, g_pszComputer) == 0 &&
|
|
pCommandArgs[eComputerInactive].bDefined))
|
|
{
|
|
INT nDomainBehaviorVersion = 0;
|
|
CComPtr<IADs> spDomain;
|
|
CComBSTR sbstrBasePath;
|
|
refBasePathsInfo.ComposePathFromDN(refBasePathsInfo.GetDefaultNamingContext(),
|
|
sbstrBasePath);
|
|
hr = DSCmdOpenObject(refCredentialObject,
|
|
sbstrBasePath,
|
|
IID_IADs,
|
|
(void**)&spDomain,
|
|
bBindToServer);
|
|
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
nDomainBehaviorVersion = 0;
|
|
}
|
|
|
|
CComVariant varVer;
|
|
hr = spDomain->GetInfo();
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CComBSTR bstrVer = L"msDS-Behavior-Version";
|
|
hr = spDomain->Get(bstrVer, &varVer);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
nDomainBehaviorVersion = 0;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(varVer.vt == VT_I4);
|
|
nDomainBehaviorVersion = static_cast<UINT>(varVer.lVal);
|
|
}
|
|
|
|
if(nDomainBehaviorVersion == 0)
|
|
{
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr,
|
|
IDS_FILTER_LAST_LOGON_VERSION);
|
|
hr = E_INVALIDARG;
|
|
|
|
return hr;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = DSCmdOpenObject(refCredentialObject,
|
|
sbstrObjectPath,
|
|
IID_IDirectorySearch,
|
|
(void**)&refspSearchObject,
|
|
bBindToServer);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//BIND to GC to search entire forest
|
|
//
|
|
CComPtr<IADsContainer> spContainer;
|
|
hr = DSCmdOpenObject(refCredentialObject,
|
|
L"GC:",
|
|
IID_IADsContainer,
|
|
(void**)&spContainer,
|
|
false);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
//
|
|
// Get an enumeration interface for the GC container to enumerate the
|
|
// contents. The "real" GC is the only child of the GC container.
|
|
//
|
|
CComPtr<IEnumVARIANT> spEnum = NULL;
|
|
hr = ADsBuildEnumerator(spContainer, &spEnum);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Now enumerate. There's only one child of the GC: object.
|
|
//
|
|
VARIANT var;
|
|
ULONG lFetch;
|
|
|
|
hr = spEnum->Next(1, &var, &lFetch);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
// Get the IDirectorySearch pointer.
|
|
if (lFetch == 1)
|
|
{
|
|
CComPtr<IDispatch> spDisp = V_DISPATCH(&var);
|
|
hr = spDisp->QueryInterface( IID_IDirectorySearch, (void**)&refspSearchObject);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//Need to put a better message here
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
} |