Leaked source code of windows server 2003
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.

778 lines
26 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000
  5. //
  6. // File: dsMod.cpp
  7. //
  8. // Contents: Defines the main function and parser tables for the DSMod
  9. // command line utility
  10. //
  11. // History: 06-Sep-2000 JeffJon Created
  12. //
  13. //
  14. //--------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "cstrings.h"
  17. #include "usage.h"
  18. #include "modtable.h"
  19. #include "resource.h"
  20. //
  21. // Function Declarations
  22. //
  23. HRESULT DoModValidation(PARG_RECORD pCommandArgs, BOOL& bErrorShown);
  24. HRESULT DoMod(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_DSMOD,TRUE);
  55. hr = E_INVALIDARG;
  56. break;
  57. }
  58. if(argc == 2)
  59. {
  60. if(IsTokenHelpSwitch(pToken + 1))
  61. {
  62. hr = S_OK;
  63. DisplayMessage(USAGE_DSMOD,TRUE);
  64. break;
  65. }
  66. }
  67. //
  68. // Find which object table entry to use from
  69. // the second command line argument
  70. //
  71. PDSOBJECTTABLEENTRY pObjectEntry = NULL;
  72. UINT idx = 0;
  73. while (true)
  74. {
  75. pObjectEntry = g_DSObjectTable[idx];
  76. if (!pObjectEntry)
  77. {
  78. break;
  79. }
  80. PWSTR pszObjectType = (pToken+1)->GetToken();
  81. if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
  82. {
  83. break;
  84. }
  85. idx++;
  86. }
  87. if (!pObjectEntry)
  88. {
  89. //
  90. // Display the error message and then break out of the false loop
  91. //
  92. DisplayMessage(USAGE_DSMOD);
  93. hr = E_INVALIDARG;
  94. break;
  95. }
  96. //
  97. // Now that we have the correct table entry, merge the command line table
  98. // for this object with the common commands
  99. //
  100. hr = MergeArgCommand(DSMOD_COMMON_COMMANDS,
  101. pObjectEntry->pParserTable,
  102. &pNewCommandArgs);
  103. if (FAILED(hr))
  104. {
  105. //
  106. // Display the error message and then break out of the false loop
  107. //
  108. DisplayErrorMessage(g_pszDSCommandName, L"", hr);
  109. break;
  110. }
  111. if (!pNewCommandArgs)
  112. {
  113. //
  114. // Display the usage text and then break out of the false loop
  115. //
  116. DisplayMessage(pObjectEntry->nUsageID);
  117. hr = E_FAIL;
  118. break;
  119. }
  120. PARSE_ERROR Error;
  121. if(!ParseCmd(g_pszDSCommandName,
  122. pNewCommandArgs,
  123. argc-1,
  124. pToken+1,
  125. pObjectEntry->nUsageID,
  126. &Error,
  127. TRUE))
  128. {
  129. //ParseCmd did not display any error. Error should
  130. //be handled here. Check DisplayParseError for the
  131. //cases where Error is not shown by ParseCmd
  132. if(Error.ErrorSource == ERROR_FROM_PARSER &&
  133. Error.Error == PARSE_ERROR_ATLEASTONE_NOTDEFINED)
  134. {
  135. //Show DSMOD specific error.
  136. hr = E_INVALIDARG;
  137. DisplayErrorMessage(g_pszDSCommandName,
  138. NULL,
  139. S_OK, // do not display in error message
  140. IDS_ERRMSG_ATLEASTONE);
  141. break;
  142. }
  143. else if(!Error.MessageShown)
  144. {
  145. hr = E_INVALIDARG;
  146. DisplayErrorMessage(g_pszDSCommandName,
  147. NULL,
  148. hr);
  149. break;
  150. }
  151. if(Error.ErrorSource == ERROR_FROM_PARSER
  152. && Error.Error == PARSE_ERROR_HELP_SWITCH)
  153. {
  154. hr = S_OK;
  155. break;
  156. }
  157. hr = E_INVALIDARG;
  158. break;
  159. }
  160. else
  161. {
  162. //
  163. // Check to see if we are doing debug spew
  164. //
  165. #ifdef DBG
  166. bool bDebugging = pNewCommandArgs[eCommDebug].bDefined &&
  167. pNewCommandArgs[eCommDebug].nValue;
  168. if (bDebugging)
  169. {
  170. ENABLE_DEBUG_OUTPUT(pNewCommandArgs[eCommDebug].nValue);
  171. }
  172. #else
  173. DISABLE_DEBUG_OUTPUT();
  174. #endif
  175. //
  176. // Be sure that mutually exclusive and dependent switches are correct
  177. //
  178. BOOL bErrorShown = FALSE;
  179. hr = DoModValidation(pNewCommandArgs, bErrorShown);
  180. if (FAILED(hr))
  181. {
  182. DisplayErrorMessage(g_pszDSCommandName, 0, hr);
  183. break;
  184. }
  185. //
  186. // Command line parsing succeeded
  187. //
  188. hr = DoMod(pNewCommandArgs, pObjectEntry);
  189. }
  190. } while (false);
  191. //
  192. // Free the memory associated with the command values
  193. //
  194. if (pNewCommandArgs)
  195. {
  196. FreeCmd(pNewCommandArgs);
  197. }
  198. //
  199. // Free the tokens
  200. //
  201. if (pToken)
  202. {
  203. delete[] pToken;
  204. pToken = 0;
  205. }
  206. }
  207. //
  208. // Uninitialize COM
  209. //
  210. ::CoUninitialize();
  211. return hr;
  212. }
  213. //+--------------------------------------------------------------------------
  214. //
  215. // Function: DoModValidation
  216. //
  217. // Synopsis: Checks to be sure that command line switches that are mutually
  218. // exclusive are not both present and those that are dependent are
  219. // both presetn
  220. //
  221. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  222. // to retrieve the values for each switch
  223. // [bErrorShown - OUT] : set to true if an error was shown
  224. //
  225. // Returns: HRESULT : S_OK if everything succeeded
  226. // E_INVALIDARG if the object entry wasn't found
  227. // Anything else is a failure code from an ADSI call
  228. //
  229. // History: 19-Sep-2000 JeffJon Created
  230. //
  231. //---------------------------------------------------------------------------
  232. HRESULT DoModValidation(PARG_RECORD pCommandArgs, BOOL& bErrorShown)
  233. {
  234. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoModValidation, hr);
  235. do // false loop
  236. {
  237. // Check to be sure the server and domain switches
  238. // are mutually exclusive
  239. if (pCommandArgs[eCommServer].bDefined &&
  240. pCommandArgs[eCommDomain].bDefined)
  241. {
  242. hr = E_INVALIDARG;
  243. DisplayErrorMessage(g_pszDSCommandName, 0, hr, IDS_NO_SERVER_AND_DOMAIN);
  244. bErrorShown = TRUE;
  245. break;
  246. }
  247. //
  248. // Check the user switches
  249. //
  250. if (pCommandArgs[eCommObjectType].bDefined &&
  251. pCommandArgs[eCommObjectType].strValue &&
  252. 0 == _wcsicmp(g_pszUser, pCommandArgs[eCommObjectType].strValue))
  253. {
  254. //
  255. // Can't have user must change password if user can change password is no
  256. //
  257. if ((pCommandArgs[eUserMustchpwd].bDefined &&
  258. pCommandArgs[eUserMustchpwd].bValue) &&
  259. (pCommandArgs[eUserCanchpwd].bDefined &&
  260. !pCommandArgs[eUserCanchpwd].bValue))
  261. {
  262. DisplayErrorMessage(g_pszDSCommandName, NULL, S_OK, IDS_MUSTCHPWD_CANCHPWD_CONFLICT);
  263. hr = E_INVALIDARG;
  264. break;
  265. }
  266. }
  267. if (pCommandArgs[eCommObjectType].bDefined &&
  268. pCommandArgs[eCommObjectType].strValue &&
  269. 0 == _wcsicmp(g_pszGroup, pCommandArgs[eCommObjectType].strValue))
  270. {
  271. if (pCommandArgs[eGroupAddMember].bDefined &&
  272. (!pCommandArgs[eGroupAddMember].strValue ||
  273. !pCommandArgs[eGroupAddMember].strValue[0]))
  274. {
  275. hr = E_INVALIDARG;
  276. break;
  277. }
  278. if (pCommandArgs[eGroupRemoveMember].bDefined &&
  279. (!pCommandArgs[eGroupRemoveMember].strValue ||
  280. !pCommandArgs[eGroupRemoveMember].strValue[0]))
  281. {
  282. hr = E_INVALIDARG;
  283. break;
  284. }
  285. if (pCommandArgs[eGroupChangeMember].bDefined &&
  286. (!pCommandArgs[eGroupChangeMember].strValue ||
  287. !pCommandArgs[eGroupChangeMember].strValue[0]))
  288. {
  289. hr = E_INVALIDARG;
  290. break;
  291. }
  292. }
  293. } while (false);
  294. return hr;
  295. }
  296. //+--------------------------------------------------------------------------
  297. //
  298. // Function: DoMod
  299. //
  300. // Synopsis: Finds the appropriate object in the object table and fills in
  301. // the attribute values and then applies the changes
  302. //
  303. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  304. // to retrieve the values for each switch
  305. // [pObjectEntry - IN] : pointer to the object table entry for the
  306. // object type that will be modified
  307. //
  308. // Returns: HRESULT : S_OK if everything succeeded
  309. // E_INVALIDARG if the object entry wasn't found
  310. // Anything else is a failure code from an ADSI call
  311. //
  312. // History: 07-Sep-2000 JeffJon Created
  313. //
  314. //---------------------------------------------------------------------------
  315. HRESULT DoMod(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry)
  316. {
  317. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoMod, hr);
  318. PADS_ATTR_INFO pAttrs = NULL;
  319. PWSTR pszPartitionDN = NULL;
  320. do // false loop
  321. {
  322. if (!pCommandArgs || !pObjectEntry)
  323. {
  324. ASSERT(pCommandArgs && pObjectEntry);
  325. hr = E_INVALIDARG;
  326. break;
  327. }
  328. //
  329. // The DNs or Names should be given as a \0 separated list
  330. // So parse it and loop through each object
  331. //
  332. UINT nStrings = 0;
  333. PWSTR* ppszArray = NULL;
  334. ParseNullSeparatedString(pCommandArgs[eCommObjectDNorName].strValue,
  335. &ppszArray,
  336. &nStrings);
  337. if (nStrings < 1 ||
  338. !ppszArray)
  339. {
  340. //
  341. // Display the usage text and then fail
  342. //
  343. hr = E_INVALIDARG;
  344. DisplayErrorMessage(g_pszDSCommandName, 0, hr);
  345. break;
  346. }
  347. // Make sure all the DNs actually have DN syntax
  348. bool bContinue = pCommandArgs[eCommContinue].bDefined &&
  349. pCommandArgs[eCommContinue].bValue;
  350. UINT nValidDNs = ValidateDNSyntax(ppszArray, nStrings);
  351. if (nValidDNs < nStrings && !bContinue)
  352. {
  353. hr = E_ADS_BAD_PATHNAME;
  354. DisplayErrorMessage(g_pszDSCommandName, 0, hr);
  355. break;
  356. }
  357. CDSCmdCredentialObject credentialObject;
  358. if (pCommandArgs[eCommUserName].bDefined)
  359. {
  360. credentialObject.SetUsername(pCommandArgs[eCommUserName].strValue);
  361. credentialObject.SetUsingCredentials(true);
  362. }
  363. if (pCommandArgs[eCommPassword].bDefined)
  364. {
  365. credentialObject.SetEncryptedPassword(&pCommandArgs[eCommPassword].encryptedDataBlob);
  366. credentialObject.SetUsingCredentials(true);
  367. }
  368. //
  369. // Initialize the base paths info from the command line args
  370. //
  371. CDSCmdBasePathsInfo basePathsInfo;
  372. if (pCommandArgs[eCommServer].bDefined)
  373. {
  374. hr = basePathsInfo.InitializeFromName(credentialObject,
  375. pCommandArgs[eCommServer].strValue,
  376. true);
  377. }
  378. else if (pCommandArgs[eCommDomain].bDefined)
  379. {
  380. hr = basePathsInfo.InitializeFromName(credentialObject,
  381. pCommandArgs[eCommDomain].strValue,
  382. false);
  383. }
  384. else
  385. {
  386. hr = basePathsInfo.InitializeFromName(credentialObject, NULL, false);
  387. }
  388. if (FAILED(hr))
  389. {
  390. //
  391. // Display error message and return
  392. //
  393. DEBUG_OUTPUT(MINIMAL_LOGGING, L"CDSBasePathsInfo::InitializeFromName failed: hr = 0x%x", hr);
  394. DisplayErrorMessage(g_pszDSCommandName, NULL, hr);
  395. break;
  396. }
  397. //
  398. // Now that we have the table entry loop through the other command line
  399. // args and see which ones can be applied
  400. //
  401. DWORD dwAttributeCount = 0;
  402. DWORD dwCount = pObjectEntry->dwAttributeCount;
  403. pAttrs = new ADS_ATTR_INFO[dwCount];
  404. if (!pAttrs)
  405. {
  406. //
  407. // Display error message and return
  408. //
  409. DisplayErrorMessage(g_pszDSCommandName, NULL, E_OUTOFMEMORY);
  410. hr = E_OUTOFMEMORY;
  411. break;
  412. }
  413. //
  414. // Loop through each of the objects
  415. //
  416. for (UINT nNameIdx = 0; nNameIdx < nStrings; nNameIdx++)
  417. {
  418. dwAttributeCount = 0;
  419. do // false loop
  420. {
  421. //
  422. // Get the objects DN
  423. //
  424. PWSTR pszObjectDN = ppszArray[nNameIdx];
  425. if (!pszObjectDN)
  426. {
  427. //
  428. // Display the usage text and then fail
  429. //
  430. hr = E_INVALIDARG;
  431. DisplayErrorMessage(g_pszDSCommandName, 0, hr);
  432. break;
  433. }
  434. // If partition object then look at first DN and then munge it
  435. if(0 == lstrcmpi(pObjectEntry->pszCommandLineObjectType, g_pszPartition))
  436. {
  437. // Validate the partition and get the DN to the NTDS Quotas Container
  438. hr = GetQuotaContainerDN(basePathsInfo, credentialObject,
  439. pszObjectDN, &pszPartitionDN);
  440. if(FAILED(hr))
  441. {
  442. hr = E_INVALIDARG;
  443. DisplayErrorMessage(g_pszDSCommandName,
  444. NULL,
  445. hr,
  446. IDS_ERRMSG_NO_QUOTAS_CONTAINER);
  447. break;
  448. }
  449. // Replace the object pointer with the new partition container DN
  450. pszObjectDN = pszPartitionDN;
  451. }
  452. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Object DN = %s", pszObjectDN);
  453. //
  454. // Compose the objects path
  455. //
  456. CComBSTR sbstrObjectPath;
  457. basePathsInfo.ComposePathFromDN(pszObjectDN, sbstrObjectPath);
  458. //
  459. // Verify that the object type matches the one entered on the command line
  460. //
  461. CComPtr<IDirectoryObject> spDirObject;
  462. hr = DSCmdOpenObject(credentialObject,
  463. sbstrObjectPath,
  464. IID_IDirectoryObject,
  465. (void**)&spDirObject,
  466. true);
  467. if (FAILED(hr))
  468. {
  469. //
  470. // Display error message and return
  471. //
  472. DisplayErrorMessage(g_pszDSCommandName,
  473. pszObjectDN,
  474. hr);
  475. break;
  476. }
  477. CComQIPtr<IADs> spADs(spDirObject);
  478. if (!spADs)
  479. {
  480. ASSERT(spADs);
  481. hr = E_INVALIDARG;
  482. DisplayErrorMessage(g_pszDSCommandName,
  483. pszObjectDN,
  484. hr);
  485. break;
  486. }
  487. CComBSTR sbstrClass;
  488. hr = spADs->get_Class(&sbstrClass);
  489. if (FAILED(hr))
  490. {
  491. //
  492. // Display error message and return
  493. //
  494. DisplayErrorMessage(g_pszDSCommandName,
  495. pszObjectDN,
  496. hr);
  497. break;
  498. }
  499. // 602981-2002/04/24-JonN allow inetorgperson
  500. if (_wcsicmp(sbstrClass, pObjectEntry->pszObjectClass)
  501. && ( _wcsicmp(pObjectEntry->pszObjectClass,L"user")
  502. || _wcsicmp(sbstrClass,L"inetorgperson"))
  503. // 661841-2002/07/11-JonN fix OU bug
  504. && ( _wcsicmp(pObjectEntry->pszObjectClass,L"ou")
  505. || _wcsicmp(sbstrClass,L"organizationalUnit"))
  506. )
  507. {
  508. //
  509. // Display error message and return
  510. //
  511. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Command line type does not match object class");
  512. DEBUG_OUTPUT(MINIMAL_LOGGING, L"command line type = %s", pCommandArgs[eCommObjectType].strValue);
  513. DEBUG_OUTPUT(MINIMAL_LOGGING, L"object class = %s", sbstrClass);
  514. DisplayErrorMessage(g_pszDSCommandName,
  515. pszObjectDN,
  516. hr,
  517. IDS_ERRMSG_CLASS_NOT_EQUAL);
  518. hr = E_INVALIDARG;
  519. break;
  520. }
  521. UINT nModificationsAttempted = 0;
  522. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  523. {
  524. ASSERT(pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc);
  525. UINT nAttributeIdx = pObjectEntry->pAttributeTable[dwIdx]->nAttributeID;
  526. if (pCommandArgs[nAttributeIdx].bDefined)
  527. {
  528. if (!(pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_DIRTY) ||
  529. pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE)
  530. {
  531. //
  532. // Call the evaluation function to get the ADS_ATTR_INFO set
  533. //
  534. PADS_ATTR_INFO pNewAttr = NULL;
  535. hr = pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc(pszObjectDN,
  536. basePathsInfo,
  537. credentialObject,
  538. pObjectEntry,
  539. pCommandArgs[nAttributeIdx],
  540. dwIdx,
  541. &pNewAttr);
  542. DEBUG_OUTPUT(MINIMAL_LOGGING, L"pEvalFunc returned hr = 0x%x", hr);
  543. if (SUCCEEDED(hr) && hr != S_FALSE)
  544. {
  545. if (pNewAttr)
  546. {
  547. //
  548. // Mark the attribute entry as DIRTY so that we don't have to
  549. // do the computation for the next object
  550. //
  551. pObjectEntry->pAttributeTable[dwIdx]->dwFlags |= DS_ATTRIBUTE_DIRTY;
  552. //
  553. // Copy the value
  554. //
  555. pAttrs[dwAttributeCount] = *pNewAttr;
  556. dwAttributeCount++;
  557. }
  558. }
  559. else
  560. {
  561. //
  562. // Don't show an error if the eval function returned S_FALSE
  563. //
  564. if (hr != S_FALSE)
  565. {
  566. //
  567. // Display an error
  568. //
  569. DisplayErrorMessage(g_pszDSCommandName,
  570. pszObjectDN,
  571. hr);
  572. }
  573. if (hr == S_FALSE)
  574. {
  575. //
  576. // Return a generic error code so that we don't print the success message
  577. //
  578. hr = E_FAIL;
  579. }
  580. break;
  581. }
  582. }
  583. else
  584. {
  585. //
  586. // Need to count previously retrieved values too
  587. //
  588. dwAttributeCount++;
  589. }
  590. nModificationsAttempted++;
  591. }
  592. }
  593. if (SUCCEEDED(hr) && dwAttributeCount > 0)
  594. {
  595. //
  596. // Now that we have the attributes ready, lets set them in the DS
  597. //
  598. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Setting %d attributes", dwAttributeCount);
  599. #ifdef DBG
  600. DEBUG_OUTPUT(FULL_LOGGING, L"Modified Attributes:");
  601. SpewAttrs(pAttrs, dwAttributeCount);
  602. #endif
  603. DWORD dwAttrsModified = 0;
  604. hr = spDirObject->SetObjectAttributes(pAttrs,
  605. dwAttributeCount,
  606. &dwAttrsModified);
  607. if (FAILED(hr))
  608. {
  609. //
  610. // Display error message and return
  611. //
  612. DEBUG_OUTPUT(MINIMAL_LOGGING, L"SetObjectAttributes failed: hr = 0x%x", hr);
  613. DisplayErrorMessage(g_pszDSCommandName,
  614. pszObjectDN,
  615. hr);
  616. break;
  617. }
  618. DEBUG_OUTPUT(MINIMAL_LOGGING, L"SetObjectAttributes succeeded");
  619. }
  620. else if (SUCCEEDED(hr) && nModificationsAttempted == 0)
  621. {
  622. //
  623. // Display the usage text and then break out of the false loop
  624. //
  625. hr = E_INVALIDARG;
  626. DisplayErrorMessage(g_pszDSCommandName, 0, hr);
  627. break;
  628. }
  629. } while (false);
  630. //
  631. // Loop through the attributes again, clearing any values for
  632. // attribute entries that are marked DS_ATTRIBUTE_NOT_REUSABLE
  633. //
  634. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Cleaning up memory and flags for object %d", nNameIdx);
  635. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  636. {
  637. if (pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE)
  638. {
  639. if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc &&
  640. ((pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ) ||
  641. (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_DIRTY)))
  642. {
  643. //
  644. // Cleanup the memory associated with the value
  645. //
  646. if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  647. {
  648. delete[] pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues;
  649. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues = NULL;
  650. }
  651. //
  652. // Cleanup the flags so that the attribute will be read for the next object
  653. //
  654. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_READ);
  655. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_DIRTY);
  656. DEBUG_OUTPUT(LEVEL5_LOGGING,
  657. L"Flags for attribute %s = %d",
  658. pObjectEntry->pAttributeTable[dwIdx]->pszName,
  659. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags);
  660. }
  661. }
  662. }
  663. if (FAILED(hr) && !pCommandArgs[eCommContinue].bDefined)
  664. {
  665. break;
  666. }
  667. //
  668. // Display the success message
  669. //
  670. if (SUCCEEDED(hr) && !pCommandArgs[eCommQuiet].bDefined)
  671. {
  672. DisplaySuccessMessage(g_pszDSCommandName,
  673. ppszArray[nNameIdx]);
  674. }
  675. // If we alloc'd a partition DN, then free it
  676. if(pszPartitionDN)
  677. {
  678. LocalFree(pszPartitionDN);
  679. pszPartitionDN = NULL;
  680. }
  681. } // Name for loop
  682. } while (false);
  683. // If we alloc'd a partition DN, then free it
  684. if(pszPartitionDN)
  685. {
  686. LocalFree(pszPartitionDN);
  687. pszPartitionDN = NULL;
  688. }
  689. //
  690. // Cleanup
  691. //
  692. if (pAttrs)
  693. {
  694. delete[] pAttrs;
  695. pAttrs = NULL;
  696. }
  697. return hr;
  698. }