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.

902 lines
31 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000
  5. //
  6. // File: dsAdd.cpp
  7. //
  8. // Contents: Defines the main function and parser tables for the DSAdd
  9. // command line utility
  10. //
  11. // History: 22-Sep-2000 JeffJon Created
  12. //
  13. //
  14. //--------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "cstrings.h"
  17. #include "usage.h"
  18. #include "addtable.h"
  19. #include "resource.h"
  20. //
  21. // Function Declarations
  22. //
  23. HRESULT DoAddValidation(PARG_RECORD pCommandArgs);
  24. HRESULT DoAdd(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry);
  25. int __cdecl _tmain( VOID )
  26. {
  27. int argc;
  28. LPTOKEN pToken = NULL;
  29. HRESULT hr = S_OK;
  30. //
  31. // Initialize COM
  32. //
  33. hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  34. if (FAILED(hr))
  35. {
  36. DisplayErrorMessage(g_pszDSCommandName,
  37. NULL,
  38. hr);
  39. return hr;
  40. }
  41. if( !GetCommandInput(&argc,&pToken) )
  42. {
  43. PARG_RECORD pNewCommandArgs = 0;
  44. //
  45. // False loop
  46. //
  47. do
  48. {
  49. if(argc == 1)
  50. {
  51. //
  52. // Display the error message and then break out of the false loop
  53. //
  54. DisplayMessage(USAGE_DSADD);
  55. hr = E_INVALIDARG;
  56. break;
  57. }
  58. //
  59. // Find which object table entry to use from
  60. // the second command line argument
  61. //
  62. PDSOBJECTTABLEENTRY pObjectEntry = NULL;
  63. UINT idx = 0;
  64. while (true)
  65. {
  66. pObjectEntry = g_DSObjectTable[idx];
  67. if (!pObjectEntry)
  68. {
  69. break;
  70. }
  71. PWSTR pszObjectType = (pToken+1)->GetToken();
  72. if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
  73. {
  74. break;
  75. }
  76. idx++;
  77. }
  78. if (!pObjectEntry)
  79. {
  80. //
  81. // Display the error message and then break out of the false loop
  82. //
  83. DisplayMessage(USAGE_DSADD);
  84. hr = E_INVALIDARG;
  85. break;
  86. }
  87. //
  88. // Now that we have the correct table entry, merge the command line table
  89. // for this object with the common commands
  90. //
  91. hr = MergeArgCommand(DSADD_COMMON_COMMANDS,
  92. pObjectEntry->pParserTable,
  93. &pNewCommandArgs);
  94. if (FAILED(hr))
  95. {
  96. //
  97. // Display the error message and then break out of the false loop
  98. //
  99. DisplayErrorMessage(g_pszDSCommandName, L"", hr);
  100. break;
  101. }
  102. if (!pNewCommandArgs)
  103. {
  104. //
  105. // Display the usage text and then break out of the false loop
  106. //
  107. DisplayMessage(pObjectEntry->nUsageID);
  108. hr = E_FAIL;
  109. break;
  110. }
  111. PARSE_ERROR Error;
  112. if(!ParseCmd(pNewCommandArgs,
  113. argc-1,
  114. pToken+1,
  115. pObjectEntry->nUsageID,
  116. &Error,
  117. TRUE))
  118. {
  119. if (Error.Error != PARSE_ERROR_HELP_SWITCH &&
  120. Error.Error != ERROR_FROM_PARSER)
  121. {
  122. //
  123. // Display the usage text and then break out of the false loop
  124. //
  125. DisplayMessage(pObjectEntry->nUsageID);
  126. }
  127. hr = E_INVALIDARG;
  128. break;
  129. }
  130. else
  131. {
  132. //
  133. // Check to see if we are doing debug spew
  134. //
  135. #ifdef DBG
  136. bool bDebugging = pNewCommandArgs[eCommDebug].bDefined &&
  137. pNewCommandArgs[eCommDebug].nValue;
  138. if (bDebugging)
  139. {
  140. ENABLE_DEBUG_OUTPUT(pNewCommandArgs[eCommDebug].nValue);
  141. }
  142. #else
  143. DISABLE_DEBUG_OUTPUT();
  144. #endif
  145. //
  146. // Be sure that mutually exclusive and dependent switches are correct
  147. //
  148. hr = DoAddValidation(pNewCommandArgs);
  149. if (FAILED(hr))
  150. {
  151. DisplayErrorMessage(g_pszDSCommandName,
  152. pNewCommandArgs[eCommObjectDNorName].strValue,
  153. hr);
  154. break;
  155. }
  156. //
  157. // Command line parsing succeeded
  158. //
  159. hr = DoAdd(pNewCommandArgs, pObjectEntry);
  160. }
  161. } while (false);
  162. //
  163. // Free the memory associated with the command values
  164. //
  165. if (pNewCommandArgs)
  166. {
  167. FreeCmd(pNewCommandArgs);
  168. }
  169. //
  170. // Free the tokens
  171. //
  172. if (pToken)
  173. {
  174. delete[] pToken;
  175. pToken = 0;
  176. }
  177. }
  178. //
  179. // Uninitialize COM
  180. //
  181. ::CoUninitialize();
  182. return hr;
  183. }
  184. //+--------------------------------------------------------------------------
  185. //
  186. // Function: DoGroupValidation
  187. //
  188. // Synopsis: Checks to be sure that command line switches for a group that
  189. // are mutually exclusive are not both present and those that
  190. // are dependent are both present
  191. //
  192. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  193. // to retrieve the values for each switch
  194. //
  195. // Returns: HRESULT : S_OK if everything succeeded
  196. // E_INVALIDARG if the object entry wasn't found
  197. // Anything else is a failure code from an ADSI call
  198. //
  199. // History: 04-Oct-2000 JeffJon Created
  200. //
  201. //---------------------------------------------------------------------------
  202. HRESULT DoGroupValidation(PARG_RECORD pCommandArgs)
  203. {
  204. HRESULT hr = S_OK;
  205. do // false loop
  206. {
  207. //
  208. // Set the group scope to default (global) if not given
  209. //
  210. if (!pCommandArgs[eGroupScope].bDefined ||
  211. !pCommandArgs[eGroupScope].strValue)
  212. {
  213. size_t nScopeLen = _tcslen(g_bstrGroupScopeGlobal);
  214. pCommandArgs[eGroupScope].strValue = (LPTSTR)LocalAlloc(LPTR, (nScopeLen+2) * sizeof(TCHAR) );
  215. if (!pCommandArgs[eGroupScope].strValue)
  216. {
  217. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to allocate space for pCommandArgs[eGroupScope].strValue");
  218. hr = E_OUTOFMEMORY;
  219. break;
  220. }
  221. _tcscpy(pCommandArgs[eGroupScope].strValue, g_bstrGroupScopeGlobal);
  222. pCommandArgs[eGroupScope].bDefined = TRUE;
  223. }
  224. //
  225. // Set the group security to default (yes) if not given
  226. //
  227. if (!pCommandArgs[eGroupSecgrp].bDefined)
  228. {
  229. pCommandArgs[eGroupSecgrp].bValue = TRUE;
  230. pCommandArgs[eGroupSecgrp].bDefined = TRUE;
  231. //
  232. // Need to change the type to bool so that FreeCmd doesn't
  233. // try to free the string when the value is true
  234. //
  235. pCommandArgs[eGroupSecgrp].fType = ARG_TYPE_BOOL;
  236. }
  237. } while (false);
  238. return hr;
  239. }
  240. //+--------------------------------------------------------------------------
  241. //
  242. // Function: DoAddValidation
  243. //
  244. // Synopsis: Checks to be sure that command line switches that are mutually
  245. // exclusive are not both present and those that are dependent are
  246. // both presetn
  247. //
  248. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  249. // to retrieve the values for each switch
  250. //
  251. // Returns: HRESULT : S_OK if everything succeeded
  252. // E_INVALIDARG if the object entry wasn't found
  253. // Anything else is a failure code from an ADSI call
  254. //
  255. // History: 22-Sep-2000 JeffJon Created
  256. //
  257. //---------------------------------------------------------------------------
  258. HRESULT DoAddValidation(PARG_RECORD pCommandArgs)
  259. {
  260. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoAddValidation, hr);
  261. do // false loop
  262. {
  263. //
  264. // Check the user switches
  265. //
  266. PWSTR pszObjectType = NULL;
  267. if (!pCommandArgs[eCommObjectType].bDefined &&
  268. !pCommandArgs[eCommObjectType].strValue)
  269. {
  270. hr = E_INVALIDARG;
  271. break;
  272. }
  273. pszObjectType = pCommandArgs[eCommObjectType].strValue;
  274. if (0 == _wcsicmp(g_pszUser, pszObjectType))
  275. {
  276. //
  277. // Can't have user must change password if user can change password is no
  278. //
  279. if ((pCommandArgs[eUserMustchpwd].bDefined &&
  280. pCommandArgs[eUserMustchpwd].bValue) &&
  281. (pCommandArgs[eUserCanchpwd].bDefined &&
  282. !pCommandArgs[eUserCanchpwd].bValue))
  283. {
  284. DEBUG_OUTPUT(MINIMAL_LOGGING, L"User must change password and user can change password = false was supplied");
  285. hr = E_INVALIDARG;
  286. break;
  287. }
  288. }
  289. else if (0 == _wcsicmp(g_pszGroup, pszObjectType))
  290. {
  291. hr = DoGroupValidation(pCommandArgs);
  292. break;
  293. }
  294. } while (false);
  295. return hr;
  296. }
  297. //+--------------------------------------------------------------------------
  298. //
  299. // Function: DoAdd
  300. //
  301. // Synopsis: Finds the appropriate object in the object table and fills in
  302. // the attribute values and then creates the object
  303. //
  304. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  305. // to retrieve the values for each switch
  306. // [pObjectEntry - IN] : pointer to the object table entry for the
  307. // object type that will be modified
  308. //
  309. // Returns: HRESULT : S_OK if everything succeeded
  310. // E_INVALIDARG if the object entry wasn't found
  311. // Anything else is a failure code from an ADSI call
  312. //
  313. // History: 22-Sep-2000 JeffJon Created
  314. //
  315. //---------------------------------------------------------------------------
  316. HRESULT DoAdd(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry)
  317. {
  318. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoAdd, hr);
  319. PADS_ATTR_INFO pCreateAttrs = NULL;
  320. PADS_ATTR_INFO pPostCreateAttrs = NULL;
  321. do // false loop
  322. {
  323. if (!pCommandArgs || !pObjectEntry)
  324. {
  325. ASSERT(pCommandArgs && pObjectEntry);
  326. hr = E_INVALIDARG;
  327. break;
  328. }
  329. CDSCmdCredentialObject credentialObject;
  330. if (pCommandArgs[eCommUserName].bDefined)
  331. {
  332. credentialObject.SetUsername(pCommandArgs[eCommUserName].strValue);
  333. credentialObject.SetUsingCredentials(true);
  334. }
  335. if (pCommandArgs[eCommPassword].bDefined)
  336. {
  337. credentialObject.SetPassword(pCommandArgs[eCommPassword].strValue);
  338. credentialObject.SetUsingCredentials(true);
  339. }
  340. //
  341. // Initialize the base paths info from the command line args
  342. //
  343. CDSCmdBasePathsInfo basePathsInfo;
  344. if (pCommandArgs[eCommServer].bDefined)
  345. {
  346. hr = basePathsInfo.InitializeFromName(credentialObject,
  347. pCommandArgs[eCommServer].strValue,
  348. true);
  349. }
  350. else if (pCommandArgs[eCommDomain].bDefined)
  351. {
  352. hr = basePathsInfo.InitializeFromName(credentialObject,
  353. pCommandArgs[eCommDomain].strValue,
  354. false);
  355. }
  356. else
  357. {
  358. hr = basePathsInfo.InitializeFromName(credentialObject, NULL, false);
  359. }
  360. if (FAILED(hr))
  361. {
  362. //
  363. // Display error message and return
  364. //
  365. DisplayErrorMessage(g_pszDSCommandName, NULL, hr);
  366. break;
  367. }
  368. //
  369. // The DNs or Names should be given as a \0 separated list
  370. // So parse it and loop through each object
  371. //
  372. UINT nStrings = 0;
  373. PWSTR* ppszArray = NULL;
  374. ParseNullSeparatedString(pCommandArgs[eCommObjectDNorName].strValue,
  375. &ppszArray,
  376. &nStrings);
  377. if (nStrings < 1 ||
  378. !ppszArray)
  379. {
  380. //
  381. // Display the usage text and then fail
  382. //
  383. DisplayMessage(pObjectEntry->nUsageID);
  384. hr = E_INVALIDARG;
  385. break;
  386. }
  387. DWORD dwCount = pObjectEntry->dwAttributeCount;
  388. //
  389. // Allocate the creation ADS_ATTR_INFO
  390. // Add an extra attribute for the object class
  391. //
  392. pCreateAttrs = new ADS_ATTR_INFO[dwCount + 1];
  393. if (!pCreateAttrs)
  394. {
  395. //
  396. // Display error message and return
  397. //
  398. DisplayErrorMessage(g_pszDSCommandName, NULL, E_OUTOFMEMORY);
  399. hr = E_OUTOFMEMORY;
  400. break;
  401. }
  402. //
  403. // Allocate the post create ADS_ATTR_INFO
  404. //
  405. pPostCreateAttrs = new ADS_ATTR_INFO[dwCount];
  406. if (!pPostCreateAttrs)
  407. {
  408. //
  409. // Display error message and return
  410. //
  411. DisplayErrorMessage(g_pszDSCommandName, NULL, E_OUTOFMEMORY);
  412. hr = E_OUTOFMEMORY;
  413. break;
  414. }
  415. //
  416. // Loop through each of the objects
  417. //
  418. for (UINT nNameIdx = 0; nNameIdx < nStrings; nNameIdx++)
  419. {
  420. do // false loop
  421. {
  422. //
  423. // Get the objects DN
  424. //
  425. PWSTR pszObjectDN = ppszArray[nNameIdx];
  426. if (!pszObjectDN)
  427. {
  428. //
  429. // Display the usage text and then fail
  430. //
  431. DisplayMessage(pObjectEntry->nUsageID);
  432. hr = E_INVALIDARG;
  433. break; // this breaks out of the false loop
  434. }
  435. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Object DN = %s", pszObjectDN);
  436. CComBSTR sbstrObjectPath;
  437. basePathsInfo.ComposePathFromDN(pszObjectDN, sbstrObjectPath);
  438. //
  439. // Now that we have the table entry loop through the other command line
  440. // args and see which ones can be applied
  441. //
  442. DWORD dwCreateAttributeCount = 0;
  443. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Starting processing DS_ATTRIBUTE_ONCREATE attributes");
  444. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  445. {
  446. ASSERT(pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc);
  447. UINT nAttributeIdx = pObjectEntry->pAttributeTable[dwIdx]->nAttributeID;
  448. if (pCommandArgs[nAttributeIdx].bDefined ||
  449. pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_REQUIRED)
  450. {
  451. //
  452. // Call the evaluation function to get the appropriate ADS_ATTR_INFO set
  453. // if this attribute entry has the DS_ATTRIBUTE_ONCREATE flag set
  454. //
  455. if ((pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_ONCREATE) &&
  456. (!(pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_DIRTY) ||
  457. pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE))
  458. {
  459. PADS_ATTR_INFO pNewAttr = NULL;
  460. hr = pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc(pszObjectDN,
  461. basePathsInfo,
  462. credentialObject,
  463. pObjectEntry,
  464. pCommandArgs[nAttributeIdx],
  465. dwIdx,
  466. &pNewAttr);
  467. DEBUG_OUTPUT(MINIMAL_LOGGING, L"pEvalFunc returned hr = 0x%x", hr);
  468. if (SUCCEEDED(hr) && hr != S_FALSE)
  469. {
  470. if (pNewAttr)
  471. {
  472. pCreateAttrs[dwCreateAttributeCount] = *pNewAttr;
  473. dwCreateAttributeCount++;
  474. }
  475. }
  476. else
  477. {
  478. //
  479. // Don't show an error if the eval function returned S_FALSE
  480. //
  481. if (hr != S_FALSE)
  482. {
  483. //
  484. // Display an error
  485. //
  486. DisplayErrorMessage(g_pszDSCommandName,
  487. pszObjectDN,
  488. hr);
  489. }
  490. if (hr == S_FALSE)
  491. {
  492. //
  493. // Return a generic error code so that we don't print the success message
  494. //
  495. hr = E_FAIL;
  496. }
  497. break; // this breaks out of the attribute loop
  498. }
  499. }
  500. }
  501. } // Attribute for loop
  502. //
  503. // The IDispatch interface of the new object
  504. //
  505. CComPtr<IDispatch> spDispatch;
  506. if (SUCCEEDED(hr))
  507. {
  508. //
  509. // Now that we have the attributes ready, lets create the object
  510. //
  511. //
  512. // Get the parent path of the new object
  513. //
  514. CComBSTR sbstrParentDN;
  515. hr = CPathCracker::GetParentDN(pszObjectDN, sbstrParentDN);
  516. if (FAILED(hr))
  517. {
  518. //
  519. // Display error message and return
  520. //
  521. DisplayErrorMessage(g_pszDSCommandName,
  522. pszObjectDN,
  523. hr);
  524. break; // this breaks out of the false loop
  525. }
  526. CComBSTR sbstrParentPath;
  527. basePathsInfo.ComposePathFromDN(sbstrParentDN, sbstrParentPath);
  528. //
  529. // Open the parent of the new object
  530. //
  531. CComPtr<IDirectoryObject> spDirObject;
  532. hr = DSCmdOpenObject(credentialObject,
  533. sbstrParentPath,
  534. IID_IDirectoryObject,
  535. (void**)&spDirObject,
  536. true);
  537. if (FAILED(hr))
  538. {
  539. //
  540. // Display error message and return
  541. //
  542. DisplayErrorMessage(g_pszDSCommandName,
  543. pszObjectDN,
  544. hr);
  545. break; // this breaks out of the false loop
  546. }
  547. //
  548. // Get the name of the new object
  549. //
  550. CComBSTR sbstrObjectName;
  551. hr = CPathCracker::GetObjectRDNFromDN(pszObjectDN, sbstrObjectName);
  552. if (FAILED(hr))
  553. {
  554. //
  555. // Display error message and return
  556. //
  557. DisplayErrorMessage(g_pszDSCommandName,
  558. pszObjectDN,
  559. hr);
  560. break; // this breaks out of the false loop
  561. }
  562. //
  563. // Add the object class to the attributes before creating the object
  564. //
  565. PADSVALUE pADsObjectClassValue = new ADSVALUE;
  566. if (!pADsObjectClassValue)
  567. {
  568. hr = E_OUTOFMEMORY;
  569. //
  570. // Display error message and return
  571. //
  572. DisplayErrorMessage(g_pszDSCommandName,
  573. pszObjectDN,
  574. hr);
  575. break; // this breaks out of the false loop
  576. }
  577. pADsObjectClassValue->dwType = ADSTYPE_CASE_IGNORE_STRING;
  578. pADsObjectClassValue->CaseIgnoreString = (PWSTR)pObjectEntry->pszObjectClass;
  579. DEBUG_OUTPUT(MINIMAL_LOGGING, L"New object name = %s", pObjectEntry->pszObjectClass);
  580. ADS_ATTR_INFO adsClassAttrInfo =
  581. {
  582. L"objectClass",
  583. ADS_ATTR_UPDATE,
  584. ADSTYPE_CASE_IGNORE_STRING,
  585. pADsObjectClassValue,
  586. 1
  587. };
  588. pCreateAttrs[dwCreateAttributeCount] = adsClassAttrInfo;
  589. dwCreateAttributeCount++;
  590. #ifdef DBG
  591. DEBUG_OUTPUT(FULL_LOGGING, L"Creation Attributes:");
  592. SpewAttrs(pCreateAttrs, dwCreateAttributeCount);
  593. #endif
  594. hr = spDirObject->CreateDSObject(sbstrObjectName,
  595. pCreateAttrs,
  596. dwCreateAttributeCount,
  597. &spDispatch);
  598. DEBUG_OUTPUT(MINIMAL_LOGGING, L"CreateDSObject returned hr = 0x%x", hr);
  599. if (FAILED(hr))
  600. {
  601. CComBSTR sbstrDuplicateErrorMessage;
  602. if (ERROR_OBJECT_ALREADY_EXISTS == HRESULT_CODE(hr))
  603. {
  604. sbstrDuplicateErrorMessage.LoadString(::GetModuleHandle(NULL),
  605. IDS_MSG_DUPLICATE_NAME_ERROR);
  606. }
  607. //
  608. // Display error message and return
  609. //
  610. DisplayErrorMessage(g_pszDSCommandName,
  611. pszObjectDN,
  612. hr,
  613. sbstrDuplicateErrorMessage);
  614. if (pADsObjectClassValue)
  615. {
  616. delete pADsObjectClassValue;
  617. pADsObjectClassValue = NULL;
  618. }
  619. break; // this breaks out of the false loop
  620. }
  621. if (pADsObjectClassValue)
  622. {
  623. delete pADsObjectClassValue;
  624. pADsObjectClassValue = NULL;
  625. }
  626. }
  627. if (SUCCEEDED(hr))
  628. {
  629. //
  630. // Now that we have created the object, set the attributes that are
  631. // marked for Post Create
  632. //
  633. DWORD dwPostCreateAttributeCount = 0;
  634. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Starting processing DS_ATTRIBUTE_POSTCREATE attributes");
  635. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  636. {
  637. ASSERT(pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc);
  638. UINT nAttributeIdx = pObjectEntry->pAttributeTable[dwIdx]->nAttributeID;
  639. if (pCommandArgs[nAttributeIdx].bDefined ||
  640. pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_REQUIRED)
  641. {
  642. //
  643. // Call the evaluation function to get the appropriate ADS_ATTR_INFO set
  644. // if this attribute entry has the DS_ATTRIBUTE_POSTCREATE flag set
  645. //
  646. if ((pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_POSTCREATE) &&
  647. (!(pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_DIRTY) ||
  648. pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE))
  649. {
  650. PADS_ATTR_INFO pNewAttr = NULL;
  651. hr = pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc(pszObjectDN,
  652. basePathsInfo,
  653. credentialObject,
  654. pObjectEntry,
  655. pCommandArgs[nAttributeIdx],
  656. dwIdx,
  657. &pNewAttr);
  658. DEBUG_OUTPUT(MINIMAL_LOGGING, L"pEvalFunc returned hr = 0x%x", hr);
  659. if (SUCCEEDED(hr) && hr != S_FALSE)
  660. {
  661. if (pNewAttr)
  662. {
  663. pPostCreateAttrs[dwPostCreateAttributeCount] = *pNewAttr;
  664. dwPostCreateAttributeCount++;
  665. }
  666. }
  667. else
  668. {
  669. //
  670. // Don't show an error if the eval function returned S_FALSE
  671. //
  672. if (hr != S_FALSE)
  673. {
  674. //
  675. // Load the post create message
  676. //
  677. CComBSTR sbstrPostCreateMessage;
  678. sbstrPostCreateMessage.LoadString(::GetModuleHandle(NULL),
  679. IDS_POST_CREATE_FAILURE);
  680. //
  681. // Display an error
  682. //
  683. DisplayErrorMessage(g_pszDSCommandName,
  684. pszObjectDN,
  685. hr,
  686. sbstrPostCreateMessage);
  687. }
  688. if (hr == S_FALSE)
  689. {
  690. //
  691. // Return a generic error code so that we don't print the success message
  692. //
  693. hr = E_FAIL;
  694. }
  695. break; // attribute table loop
  696. }
  697. }
  698. }
  699. } // Attribute table for loop
  700. //
  701. // Now set the attributes if necessary
  702. //
  703. if (SUCCEEDED(hr) && dwPostCreateAttributeCount > 0)
  704. {
  705. //
  706. // Now that we have the attributes ready, lets set them in the DS
  707. //
  708. CComPtr<IDirectoryObject> spNewDirObject;
  709. hr = spDispatch->QueryInterface(IID_IDirectoryObject, (void**)&spNewDirObject);
  710. if (FAILED(hr))
  711. {
  712. //
  713. // Display error message and return
  714. //
  715. DEBUG_OUTPUT(MINIMAL_LOGGING, L"QI for IDirectoryObject failed: hr = 0x%x", hr);
  716. DisplayErrorMessage(g_pszDSCommandName,
  717. pszObjectDN,
  718. hr);
  719. break; // this breaks out of the false loop
  720. }
  721. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Setting %d attributes", dwPostCreateAttributeCount);
  722. #ifdef DBG
  723. DEBUG_OUTPUT(FULL_LOGGING, L"Post Creation Attributes:");
  724. SpewAttrs(pPostCreateAttrs, dwPostCreateAttributeCount);
  725. #endif
  726. DWORD dwAttrsModified = 0;
  727. hr = spNewDirObject->SetObjectAttributes(pPostCreateAttrs,
  728. dwPostCreateAttributeCount,
  729. &dwAttrsModified);
  730. DEBUG_OUTPUT(MINIMAL_LOGGING, L"SetObjectAttributes returned hr = 0x%x", hr);
  731. if (FAILED(hr))
  732. {
  733. //
  734. // Display error message and return
  735. //
  736. DisplayErrorMessage(g_pszDSCommandName,
  737. pszObjectDN,
  738. hr);
  739. break; // this breaks out of the false loop
  740. }
  741. }
  742. }
  743. } while (false);
  744. //
  745. // Loop through the attributes again, clearing any values for
  746. // attribute entries that are marked DS_ATTRIBUTE_NOT_REUSABLE
  747. //
  748. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Cleaning up memory and flags for object %d", nNameIdx);
  749. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  750. {
  751. if (pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE)
  752. {
  753. if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc &&
  754. ((pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ) ||
  755. (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_DIRTY)))
  756. {
  757. //
  758. // Cleanup the memory associated with the value
  759. //
  760. if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  761. {
  762. delete[] pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues;
  763. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues = NULL;
  764. }
  765. //
  766. // Cleanup the flags so that the attribute will be read for the next object
  767. //
  768. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_READ);
  769. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_DIRTY);
  770. DEBUG_OUTPUT(LEVEL5_LOGGING,
  771. L"Flags for attribute %s = %d",
  772. pObjectEntry->pAttributeTable[dwIdx]->pszName,
  773. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags);
  774. }
  775. }
  776. }
  777. //
  778. // Break if the continue flag is not specified
  779. //
  780. if (FAILED(hr) && !pCommandArgs[eCommContinue].bDefined)
  781. {
  782. break; // this breaks out of the name for loop
  783. }
  784. //
  785. // Display the success message
  786. //
  787. if (SUCCEEDED(hr) && !pCommandArgs[eCommQuiet].bDefined)
  788. {
  789. DisplaySuccessMessage(g_pszDSCommandName,
  790. pCommandArgs[eCommObjectDNorName].strValue);
  791. }
  792. } // Names for loop
  793. } while (false);
  794. //
  795. // Cleanup
  796. //
  797. if (pCreateAttrs)
  798. {
  799. delete[] pCreateAttrs;
  800. pCreateAttrs = NULL;
  801. }
  802. if (pPostCreateAttrs)
  803. {
  804. delete[] pPostCreateAttrs;
  805. pPostCreateAttrs = NULL;
  806. }
  807. return hr;
  808. }