mirror of https://github.com/tongzx/nt5src
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.
902 lines
31 KiB
902 lines
31 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 2000
|
|
//
|
|
// File: dsAdd.cpp
|
|
//
|
|
// Contents: Defines the main function and parser tables for the DSAdd
|
|
// command line utility
|
|
//
|
|
// History: 22-Sep-2000 JeffJon Created
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#include "cstrings.h"
|
|
#include "usage.h"
|
|
#include "addtable.h"
|
|
#include "resource.h"
|
|
|
|
//
|
|
// Function Declarations
|
|
//
|
|
HRESULT DoAddValidation(PARG_RECORD pCommandArgs);
|
|
HRESULT DoAdd(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry);
|
|
|
|
|
|
int __cdecl _tmain( VOID )
|
|
{
|
|
|
|
int argc;
|
|
LPTOKEN pToken = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Initialize COM
|
|
//
|
|
hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
NULL,
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
if( !GetCommandInput(&argc,&pToken) )
|
|
{
|
|
PARG_RECORD pNewCommandArgs = 0;
|
|
|
|
//
|
|
// False loop
|
|
//
|
|
do
|
|
{
|
|
if(argc == 1)
|
|
{
|
|
//
|
|
// Display the error message and then break out of the false loop
|
|
//
|
|
DisplayMessage(USAGE_DSADD);
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find which object table entry to use from
|
|
// the second command line argument
|
|
//
|
|
PDSOBJECTTABLEENTRY pObjectEntry = NULL;
|
|
UINT idx = 0;
|
|
while (true)
|
|
{
|
|
pObjectEntry = g_DSObjectTable[idx];
|
|
if (!pObjectEntry)
|
|
{
|
|
break;
|
|
}
|
|
|
|
PWSTR pszObjectType = (pToken+1)->GetToken();
|
|
if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
|
|
{
|
|
break;
|
|
}
|
|
idx++;
|
|
}
|
|
|
|
if (!pObjectEntry)
|
|
{
|
|
//
|
|
// Display the error message and then break out of the false loop
|
|
//
|
|
DisplayMessage(USAGE_DSADD);
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now that we have the correct table entry, merge the command line table
|
|
// for this object with the common commands
|
|
//
|
|
hr = MergeArgCommand(DSADD_COMMON_COMMANDS,
|
|
pObjectEntry->pParserTable,
|
|
&pNewCommandArgs);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Display the error message and then break out of the false loop
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName, L"", hr);
|
|
break;
|
|
}
|
|
|
|
if (!pNewCommandArgs)
|
|
{
|
|
//
|
|
// Display the usage text and then break out of the false loop
|
|
//
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
PARSE_ERROR Error;
|
|
if(!ParseCmd(pNewCommandArgs,
|
|
argc-1,
|
|
pToken+1,
|
|
pObjectEntry->nUsageID,
|
|
&Error,
|
|
TRUE))
|
|
{
|
|
if (Error.Error != PARSE_ERROR_HELP_SWITCH &&
|
|
Error.Error != ERROR_FROM_PARSER)
|
|
{
|
|
//
|
|
// Display the usage text and then break out of the false loop
|
|
//
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
}
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// 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
|
|
//
|
|
// Be sure that mutually exclusive and dependent switches are correct
|
|
//
|
|
hr = DoAddValidation(pNewCommandArgs);
|
|
if (FAILED(hr))
|
|
{
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pNewCommandArgs[eCommObjectDNorName].strValue,
|
|
hr);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Command line parsing succeeded
|
|
//
|
|
hr = DoAdd(pNewCommandArgs, pObjectEntry);
|
|
}
|
|
|
|
} while (false);
|
|
|
|
//
|
|
// 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: DoGroupValidation
|
|
//
|
|
// Synopsis: Checks to be sure that command line switches for a group that
|
|
// are mutually exclusive are not both present and those that
|
|
// are dependent are both present
|
|
//
|
|
// Arguments: [pCommandArgs - IN] : the command line argument structure used
|
|
// to retrieve the values for each switch
|
|
//
|
|
// 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: 04-Oct-2000 JeffJon Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT DoGroupValidation(PARG_RECORD pCommandArgs)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do // false loop
|
|
{
|
|
//
|
|
// Set the group scope to default (global) if not given
|
|
//
|
|
if (!pCommandArgs[eGroupScope].bDefined ||
|
|
!pCommandArgs[eGroupScope].strValue)
|
|
{
|
|
size_t nScopeLen = _tcslen(g_bstrGroupScopeGlobal);
|
|
pCommandArgs[eGroupScope].strValue = (LPTSTR)LocalAlloc(LPTR, (nScopeLen+2) * sizeof(TCHAR) );
|
|
if (!pCommandArgs[eGroupScope].strValue)
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to allocate space for pCommandArgs[eGroupScope].strValue");
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
_tcscpy(pCommandArgs[eGroupScope].strValue, g_bstrGroupScopeGlobal);
|
|
pCommandArgs[eGroupScope].bDefined = TRUE;
|
|
}
|
|
|
|
//
|
|
// Set the group security to default (yes) if not given
|
|
//
|
|
if (!pCommandArgs[eGroupSecgrp].bDefined)
|
|
{
|
|
pCommandArgs[eGroupSecgrp].bValue = TRUE;
|
|
pCommandArgs[eGroupSecgrp].bDefined = TRUE;
|
|
|
|
//
|
|
// Need to change the type to bool so that FreeCmd doesn't
|
|
// try to free the string when the value is true
|
|
//
|
|
pCommandArgs[eGroupSecgrp].fType = ARG_TYPE_BOOL;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: DoAddValidation
|
|
//
|
|
// 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
|
|
//
|
|
// Arguments: [pCommandArgs - IN] : the command line argument structure used
|
|
// to retrieve the values for each switch
|
|
//
|
|
// 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: 22-Sep-2000 JeffJon Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT DoAddValidation(PARG_RECORD pCommandArgs)
|
|
{
|
|
ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoAddValidation, hr);
|
|
|
|
do // false loop
|
|
{
|
|
//
|
|
// Check the user switches
|
|
//
|
|
PWSTR pszObjectType = NULL;
|
|
if (!pCommandArgs[eCommObjectType].bDefined &&
|
|
!pCommandArgs[eCommObjectType].strValue)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
pszObjectType = pCommandArgs[eCommObjectType].strValue;
|
|
if (0 == _wcsicmp(g_pszUser, pszObjectType))
|
|
{
|
|
//
|
|
// Can't have user must change password if user can change password is no
|
|
//
|
|
if ((pCommandArgs[eUserMustchpwd].bDefined &&
|
|
pCommandArgs[eUserMustchpwd].bValue) &&
|
|
(pCommandArgs[eUserCanchpwd].bDefined &&
|
|
!pCommandArgs[eUserCanchpwd].bValue))
|
|
{
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"User must change password and user can change password = false was supplied");
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
}
|
|
else if (0 == _wcsicmp(g_pszGroup, pszObjectType))
|
|
{
|
|
hr = DoGroupValidation(pCommandArgs);
|
|
break;
|
|
}
|
|
} while (false);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: DoAdd
|
|
//
|
|
// Synopsis: Finds the appropriate object in the object table and fills in
|
|
// the attribute values and then creates the object
|
|
//
|
|
// 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
|
|
//
|
|
// 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: 22-Sep-2000 JeffJon Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
HRESULT DoAdd(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry)
|
|
{
|
|
ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoAdd, hr);
|
|
|
|
PADS_ATTR_INFO pCreateAttrs = NULL;
|
|
PADS_ATTR_INFO pPostCreateAttrs = NULL;
|
|
|
|
do // false loop
|
|
{
|
|
if (!pCommandArgs || !pObjectEntry)
|
|
{
|
|
ASSERT(pCommandArgs && pObjectEntry);
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
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))
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName, NULL, hr);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The DNs or Names should be given as a \0 separated list
|
|
// So parse it and loop through each object
|
|
//
|
|
UINT nStrings = 0;
|
|
PWSTR* ppszArray = NULL;
|
|
ParseNullSeparatedString(pCommandArgs[eCommObjectDNorName].strValue,
|
|
&ppszArray,
|
|
&nStrings);
|
|
if (nStrings < 1 ||
|
|
!ppszArray)
|
|
{
|
|
//
|
|
// Display the usage text and then fail
|
|
//
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
DWORD dwCount = pObjectEntry->dwAttributeCount;
|
|
|
|
//
|
|
// Allocate the creation ADS_ATTR_INFO
|
|
// Add an extra attribute for the object class
|
|
//
|
|
pCreateAttrs = new ADS_ATTR_INFO[dwCount + 1];
|
|
|
|
if (!pCreateAttrs)
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName, NULL, E_OUTOFMEMORY);
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate the post create ADS_ATTR_INFO
|
|
//
|
|
pPostCreateAttrs = new ADS_ATTR_INFO[dwCount];
|
|
if (!pPostCreateAttrs)
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName, NULL, E_OUTOFMEMORY);
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Loop through each of the objects
|
|
//
|
|
for (UINT nNameIdx = 0; nNameIdx < nStrings; nNameIdx++)
|
|
{
|
|
do // false loop
|
|
{
|
|
//
|
|
// Get the objects DN
|
|
//
|
|
PWSTR pszObjectDN = ppszArray[nNameIdx];
|
|
if (!pszObjectDN)
|
|
{
|
|
//
|
|
// Display the usage text and then fail
|
|
//
|
|
DisplayMessage(pObjectEntry->nUsageID);
|
|
hr = E_INVALIDARG;
|
|
break; // this breaks out of the false loop
|
|
}
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Object DN = %s", pszObjectDN);
|
|
|
|
CComBSTR sbstrObjectPath;
|
|
basePathsInfo.ComposePathFromDN(pszObjectDN, sbstrObjectPath);
|
|
|
|
//
|
|
// Now that we have the table entry loop through the other command line
|
|
// args and see which ones can be applied
|
|
//
|
|
DWORD dwCreateAttributeCount = 0;
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Starting processing DS_ATTRIBUTE_ONCREATE attributes");
|
|
|
|
for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
|
|
{
|
|
ASSERT(pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc);
|
|
|
|
UINT nAttributeIdx = pObjectEntry->pAttributeTable[dwIdx]->nAttributeID;
|
|
|
|
if (pCommandArgs[nAttributeIdx].bDefined ||
|
|
pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_REQUIRED)
|
|
{
|
|
//
|
|
// Call the evaluation function to get the appropriate ADS_ATTR_INFO set
|
|
// if this attribute entry has the DS_ATTRIBUTE_ONCREATE flag set
|
|
//
|
|
if ((pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_ONCREATE) &&
|
|
(!(pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_DIRTY) ||
|
|
pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE))
|
|
{
|
|
PADS_ATTR_INFO pNewAttr = NULL;
|
|
hr = pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc(pszObjectDN,
|
|
basePathsInfo,
|
|
credentialObject,
|
|
pObjectEntry,
|
|
pCommandArgs[nAttributeIdx],
|
|
dwIdx,
|
|
&pNewAttr);
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"pEvalFunc returned hr = 0x%x", hr);
|
|
if (SUCCEEDED(hr) && hr != S_FALSE)
|
|
{
|
|
if (pNewAttr)
|
|
{
|
|
pCreateAttrs[dwCreateAttributeCount] = *pNewAttr;
|
|
dwCreateAttributeCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't show an error if the eval function returned S_FALSE
|
|
//
|
|
if (hr != S_FALSE)
|
|
{
|
|
//
|
|
// Display an error
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
}
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
//
|
|
// Return a generic error code so that we don't print the success message
|
|
//
|
|
hr = E_FAIL;
|
|
}
|
|
break; // this breaks out of the attribute loop
|
|
}
|
|
}
|
|
}
|
|
} // Attribute for loop
|
|
|
|
//
|
|
// The IDispatch interface of the new object
|
|
//
|
|
CComPtr<IDispatch> spDispatch;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Now that we have the attributes ready, lets create the object
|
|
//
|
|
|
|
//
|
|
// Get the parent path of the new object
|
|
//
|
|
CComBSTR sbstrParentDN;
|
|
hr = CPathCracker::GetParentDN(pszObjectDN, sbstrParentDN);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
break; // this breaks out of the false loop
|
|
}
|
|
|
|
CComBSTR sbstrParentPath;
|
|
basePathsInfo.ComposePathFromDN(sbstrParentDN, sbstrParentPath);
|
|
|
|
//
|
|
// Open the parent of the new object
|
|
//
|
|
CComPtr<IDirectoryObject> spDirObject;
|
|
hr = DSCmdOpenObject(credentialObject,
|
|
sbstrParentPath,
|
|
IID_IDirectoryObject,
|
|
(void**)&spDirObject,
|
|
true);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
break; // this breaks out of the false loop
|
|
}
|
|
|
|
//
|
|
// Get the name of the new object
|
|
//
|
|
CComBSTR sbstrObjectName;
|
|
hr = CPathCracker::GetObjectRDNFromDN(pszObjectDN, sbstrObjectName);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
break; // this breaks out of the false loop
|
|
}
|
|
|
|
//
|
|
// Add the object class to the attributes before creating the object
|
|
//
|
|
PADSVALUE pADsObjectClassValue = new ADSVALUE;
|
|
if (!pADsObjectClassValue)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
break; // this breaks out of the false loop
|
|
}
|
|
|
|
pADsObjectClassValue->dwType = ADSTYPE_CASE_IGNORE_STRING;
|
|
pADsObjectClassValue->CaseIgnoreString = (PWSTR)pObjectEntry->pszObjectClass;
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"New object name = %s", pObjectEntry->pszObjectClass);
|
|
|
|
ADS_ATTR_INFO adsClassAttrInfo =
|
|
{
|
|
L"objectClass",
|
|
ADS_ATTR_UPDATE,
|
|
ADSTYPE_CASE_IGNORE_STRING,
|
|
pADsObjectClassValue,
|
|
1
|
|
};
|
|
|
|
pCreateAttrs[dwCreateAttributeCount] = adsClassAttrInfo;
|
|
dwCreateAttributeCount++;
|
|
|
|
#ifdef DBG
|
|
DEBUG_OUTPUT(FULL_LOGGING, L"Creation Attributes:");
|
|
SpewAttrs(pCreateAttrs, dwCreateAttributeCount);
|
|
#endif
|
|
|
|
hr = spDirObject->CreateDSObject(sbstrObjectName,
|
|
pCreateAttrs,
|
|
dwCreateAttributeCount,
|
|
&spDispatch);
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"CreateDSObject returned hr = 0x%x", hr);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
CComBSTR sbstrDuplicateErrorMessage;
|
|
|
|
if (ERROR_OBJECT_ALREADY_EXISTS == HRESULT_CODE(hr))
|
|
{
|
|
sbstrDuplicateErrorMessage.LoadString(::GetModuleHandle(NULL),
|
|
IDS_MSG_DUPLICATE_NAME_ERROR);
|
|
}
|
|
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr,
|
|
sbstrDuplicateErrorMessage);
|
|
|
|
if (pADsObjectClassValue)
|
|
{
|
|
delete pADsObjectClassValue;
|
|
pADsObjectClassValue = NULL;
|
|
}
|
|
break; // this breaks out of the false loop
|
|
}
|
|
|
|
if (pADsObjectClassValue)
|
|
{
|
|
delete pADsObjectClassValue;
|
|
pADsObjectClassValue = NULL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Now that we have created the object, set the attributes that are
|
|
// marked for Post Create
|
|
//
|
|
DWORD dwPostCreateAttributeCount = 0;
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Starting processing DS_ATTRIBUTE_POSTCREATE attributes");
|
|
for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
|
|
{
|
|
ASSERT(pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc);
|
|
|
|
UINT nAttributeIdx = pObjectEntry->pAttributeTable[dwIdx]->nAttributeID;
|
|
|
|
if (pCommandArgs[nAttributeIdx].bDefined ||
|
|
pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_REQUIRED)
|
|
{
|
|
//
|
|
// Call the evaluation function to get the appropriate ADS_ATTR_INFO set
|
|
// if this attribute entry has the DS_ATTRIBUTE_POSTCREATE flag set
|
|
//
|
|
if ((pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_POSTCREATE) &&
|
|
(!(pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_DIRTY) ||
|
|
pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE))
|
|
{
|
|
PADS_ATTR_INFO pNewAttr = NULL;
|
|
hr = pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc(pszObjectDN,
|
|
basePathsInfo,
|
|
credentialObject,
|
|
pObjectEntry,
|
|
pCommandArgs[nAttributeIdx],
|
|
dwIdx,
|
|
&pNewAttr);
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"pEvalFunc returned hr = 0x%x", hr);
|
|
if (SUCCEEDED(hr) && hr != S_FALSE)
|
|
{
|
|
if (pNewAttr)
|
|
{
|
|
pPostCreateAttrs[dwPostCreateAttributeCount] = *pNewAttr;
|
|
dwPostCreateAttributeCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't show an error if the eval function returned S_FALSE
|
|
//
|
|
if (hr != S_FALSE)
|
|
{
|
|
//
|
|
// Load the post create message
|
|
//
|
|
CComBSTR sbstrPostCreateMessage;
|
|
sbstrPostCreateMessage.LoadString(::GetModuleHandle(NULL),
|
|
IDS_POST_CREATE_FAILURE);
|
|
|
|
//
|
|
// Display an error
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr,
|
|
sbstrPostCreateMessage);
|
|
}
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
//
|
|
// Return a generic error code so that we don't print the success message
|
|
//
|
|
hr = E_FAIL;
|
|
}
|
|
break; // attribute table loop
|
|
}
|
|
}
|
|
}
|
|
} // Attribute table for loop
|
|
|
|
//
|
|
// Now set the attributes if necessary
|
|
//
|
|
if (SUCCEEDED(hr) && dwPostCreateAttributeCount > 0)
|
|
{
|
|
//
|
|
// Now that we have the attributes ready, lets set them in the DS
|
|
//
|
|
CComPtr<IDirectoryObject> spNewDirObject;
|
|
hr = spDispatch->QueryInterface(IID_IDirectoryObject, (void**)&spNewDirObject);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"QI for IDirectoryObject failed: hr = 0x%x", hr);
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
break; // this breaks out of the false loop
|
|
}
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"Setting %d attributes", dwPostCreateAttributeCount);
|
|
#ifdef DBG
|
|
DEBUG_OUTPUT(FULL_LOGGING, L"Post Creation Attributes:");
|
|
SpewAttrs(pPostCreateAttrs, dwPostCreateAttributeCount);
|
|
#endif
|
|
|
|
DWORD dwAttrsModified = 0;
|
|
hr = spNewDirObject->SetObjectAttributes(pPostCreateAttrs,
|
|
dwPostCreateAttributeCount,
|
|
&dwAttrsModified);
|
|
|
|
DEBUG_OUTPUT(MINIMAL_LOGGING, L"SetObjectAttributes returned hr = 0x%x", hr);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Display error message and return
|
|
//
|
|
DisplayErrorMessage(g_pszDSCommandName,
|
|
pszObjectDN,
|
|
hr);
|
|
break; // this breaks out of the false loop
|
|
}
|
|
}
|
|
}
|
|
} while (false);
|
|
|
|
//
|
|
// Loop through the attributes again, clearing any values for
|
|
// attribute entries that are marked DS_ATTRIBUTE_NOT_REUSABLE
|
|
//
|
|
DEBUG_OUTPUT(LEVEL5_LOGGING, L"Cleaning up memory and flags for object %d", nNameIdx);
|
|
for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
|
|
{
|
|
if (pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE)
|
|
{
|
|
if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc &&
|
|
((pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ) ||
|
|
(pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_DIRTY)))
|
|
{
|
|
//
|
|
// Cleanup the memory associated with the value
|
|
//
|
|
if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues)
|
|
{
|
|
delete[] pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues;
|
|
pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues = NULL;
|
|
}
|
|
|
|
//
|
|
// Cleanup the flags so that the attribute will be read for the next object
|
|
//
|
|
pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_READ);
|
|
pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_DIRTY);
|
|
|
|
DEBUG_OUTPUT(LEVEL5_LOGGING,
|
|
L"Flags for attribute %s = %d",
|
|
pObjectEntry->pAttributeTable[dwIdx]->pszName,
|
|
pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Break if the continue flag is not specified
|
|
//
|
|
if (FAILED(hr) && !pCommandArgs[eCommContinue].bDefined)
|
|
{
|
|
break; // this breaks out of the name for loop
|
|
}
|
|
|
|
//
|
|
// Display the success message
|
|
//
|
|
if (SUCCEEDED(hr) && !pCommandArgs[eCommQuiet].bDefined)
|
|
{
|
|
DisplaySuccessMessage(g_pszDSCommandName,
|
|
pCommandArgs[eCommObjectDNorName].strValue);
|
|
}
|
|
} // Names for loop
|
|
|
|
} while (false);
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
if (pCreateAttrs)
|
|
{
|
|
delete[] pCreateAttrs;
|
|
pCreateAttrs = NULL;
|
|
}
|
|
|
|
if (pPostCreateAttrs)
|
|
{
|
|
delete[] pPostCreateAttrs;
|
|
pPostCreateAttrs = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|