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.

1332 lines
36 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // confirmation page
  4. //
  5. // 12-22-97 sburns
  6. #include "headers.hxx"
  7. #include "ConfirmationPage.hpp"
  8. #include "common.hpp"
  9. #include "resource.h"
  10. #include "ProgressDialog.hpp"
  11. #include "ds.hpp"
  12. #include "state.hpp"
  13. #include "GetCredentialsDialog.hpp"
  14. #include "postop.hpp"
  15. #include <DiagnoseDcNotFound.hpp>
  16. void PromoteThreadProc(ProgressDialog& progress);
  17. ConfirmationPage::ConfirmationPage()
  18. :
  19. DCPromoWizardPage(
  20. IDD_CONFIRMATION,
  21. IDS_CONFIRMATION_PAGE_TITLE,
  22. IDS_CONFIRMATION_PAGE_SUBTITLE),
  23. needToKillSelection(false)
  24. {
  25. LOG_CTOR(ConfirmationPage);
  26. }
  27. ConfirmationPage::~ConfirmationPage()
  28. {
  29. LOG_DTOR(ConfirmationPage);
  30. }
  31. void
  32. ConfirmationPage::OnInit()
  33. {
  34. LOG_FUNCTION(ConfirmationPage::OnInit);
  35. // Since the multi-line edit control has a bug that causes it to eat
  36. // enter keypresses, we will subclass the control to make it forward
  37. // those keypresses to the page as WM_COMMAND messages
  38. // This workaround from phellyar.
  39. // NTRAID#NTBUG9-232092-2000/11/22-sburns
  40. multiLineEdit.Init(Win::GetDlgItem(hwnd, IDC_MESSAGE));
  41. }
  42. int
  43. ConfirmationPage::Validate()
  44. {
  45. LOG_FUNCTION(ConfirmationPage::Validate);
  46. // this function should never be called, as we override OnWizNext.
  47. ASSERT(false);
  48. return 0;
  49. }
  50. static
  51. String
  52. GetMessage()
  53. {
  54. LOG_FUNCTION(GetMessage);
  55. String message;
  56. State& state = State::GetInstance();
  57. String netbiosName;
  58. State::RunContext context = state.GetRunContext();
  59. if (
  60. context == State::BDC_UPGRADE
  61. || context == State::PDC_UPGRADE)
  62. {
  63. netbiosName = state.GetComputer().GetDomainNetbiosName();
  64. }
  65. else
  66. {
  67. netbiosName = state.GetNewDomainNetbiosName();
  68. }
  69. switch (state.GetOperation())
  70. {
  71. case State::REPLICA:
  72. {
  73. message =
  74. String::format(
  75. IDS_CONFIRM_MESSAGE_REPLICA,
  76. state.GetReplicaDomainDNSName().c_str());
  77. if (state.ReplicateFromMedia())
  78. {
  79. message +=
  80. String::format(
  81. IDS_CONFIRM_MESSAGE_REPLICATE_FROM_MEDIA,
  82. state.GetReplicationSourcePath().c_str());
  83. }
  84. break;
  85. }
  86. case State::FOREST:
  87. {
  88. message =
  89. String::format(
  90. IDS_CONFIRM_MESSAGE_FOREST,
  91. state.GetNewDomainDNSName().c_str(),
  92. netbiosName.c_str());
  93. break;
  94. }
  95. case State::TREE:
  96. {
  97. message =
  98. String::format(
  99. IDS_CONFIRM_MESSAGE_TREE,
  100. state.GetNewDomainDNSName().c_str(),
  101. netbiosName.c_str(),
  102. state.GetParentDomainDnsName().c_str());
  103. break;
  104. }
  105. case State::CHILD:
  106. {
  107. message =
  108. String::format(
  109. IDS_CONFIRM_MESSAGE_CHILD,
  110. state.GetNewDomainDNSName().c_str(),
  111. netbiosName.c_str(),
  112. state.GetParentDomainDnsName().c_str());
  113. break;
  114. }
  115. case State::DEMOTE:
  116. {
  117. String domain = state.GetComputer().GetDomainDnsName();
  118. if (state.IsLastDCInDomain())
  119. {
  120. message =
  121. String::format(
  122. IDS_CONFIRM_MESSAGE_DEMOTE_LAST_DC,
  123. domain.c_str());
  124. }
  125. else
  126. {
  127. if (state.IsForcedDemotion())
  128. {
  129. message = String::load(IDS_CONFIRM_MESSAGE_FORCE_DEMOTE);
  130. }
  131. else
  132. {
  133. message =
  134. String::format(IDS_CONFIRM_MESSAGE_DEMOTE, domain.c_str());
  135. }
  136. }
  137. if (state.GetAppPartitionList().size())
  138. {
  139. message += String::format(IDS_CONFIRM_MESSAGE_APP_PARTITION);
  140. }
  141. break;
  142. }
  143. case State::ABORT_BDC_UPGRADE:
  144. {
  145. message =
  146. String::format(
  147. IDS_CONFIRM_ABORT_BDC_UPGRADE,
  148. netbiosName.c_str());
  149. break;
  150. }
  151. case State::NONE:
  152. default:
  153. {
  154. ASSERT(false);
  155. break;
  156. }
  157. }
  158. return message;
  159. }
  160. bool
  161. ConfirmationPage::OnSetActive()
  162. {
  163. LOG_FUNCTION(ConfirmationPage::OnSetActive);
  164. ASSERT(State::GetInstance().GetOperation() != State::NONE);
  165. State& state = State::GetInstance();
  166. String message = GetMessage();
  167. State::Operation operation = state.GetOperation();
  168. switch (operation)
  169. {
  170. case State::REPLICA:
  171. case State::FOREST:
  172. case State::TREE:
  173. case State::CHILD:
  174. {
  175. // write the path options into the text box
  176. String pathText =
  177. String::format(
  178. IDS_CONFIRM_PATHS_MESSAGE,
  179. state.GetDatabasePath().c_str(),
  180. state.GetLogPath().c_str(),
  181. state.GetSYSVOLPath().c_str());
  182. message += pathText;
  183. if (state.ShouldInstallAndConfigureDns())
  184. {
  185. // NTRAID#NTBUG9-446484-2001/10/11-sburns
  186. if (state.ShouldConfigDnsClient())
  187. {
  188. message += String::load(IDS_CONFIRM_INSTALL_DNS_AND_CLIENT);
  189. }
  190. else
  191. {
  192. message += String::load(IDS_CONFIRM_INSTALL_DNS);
  193. }
  194. }
  195. if (operation != State::REPLICA)
  196. {
  197. if (state.ShouldAllowAnonymousAccess())
  198. {
  199. // Only show the anon access message in forest, tree, child
  200. // 394387
  201. message += String::load(IDS_CONFIRM_DO_RAS_FIXUP);
  202. }
  203. message += String::load(IDS_DOMAIN_ADMIN_PASSWORD);
  204. }
  205. break;
  206. }
  207. case State::DEMOTE:
  208. case State::ABORT_BDC_UPGRADE:
  209. {
  210. // hide the path controls: do nothing
  211. break;
  212. }
  213. case State::NONE:
  214. default:
  215. {
  216. ASSERT(false);
  217. break;
  218. }
  219. }
  220. Win::SetDlgItemText(hwnd, IDC_MESSAGE, message);
  221. needToKillSelection = true;
  222. Win::PropSheet_SetWizButtons(
  223. Win::GetParent(hwnd),
  224. PSWIZB_BACK | PSWIZB_NEXT);
  225. if (state.RunHiddenUnattended())
  226. {
  227. return ConfirmationPage::OnWizNext();
  228. }
  229. return true;
  230. }
  231. void
  232. DoOperation(
  233. HWND parentDialog,
  234. ProgressDialog::ThreadProc threadProc,
  235. int animationResID)
  236. {
  237. LOG_FUNCTION(DoOperation);
  238. ASSERT(Win::IsWindow(parentDialog));
  239. ASSERT(threadProc);
  240. ASSERT(animationResID > 0);
  241. // the ProgressDialog::OnInit actually starts the thread.
  242. ProgressDialog dialog(threadProc, animationResID);
  243. if (
  244. dialog.ModalExecute(parentDialog)
  245. == static_cast<int>(ProgressDialog::THREAD_SUCCEEDED))
  246. {
  247. LOG(L"OPERATION SUCCESSFUL");
  248. }
  249. else
  250. {
  251. LOG(L"OPERATION FAILED");
  252. }
  253. }
  254. int
  255. DetermineAnimation()
  256. {
  257. LOG_FUNCTION(DetermineAnimation);
  258. State& state = State::GetInstance();
  259. int aviID = IDR_AVI_DOMAIN;
  260. switch (state.GetOperation())
  261. {
  262. case State::REPLICA:
  263. {
  264. aviID = IDR_AVI_REPLICA;
  265. break;
  266. }
  267. case State::DEMOTE:
  268. {
  269. aviID = IDR_AVI_DEMOTE;
  270. break;
  271. }
  272. case State::FOREST:
  273. case State::TREE:
  274. case State::CHILD:
  275. case State::ABORT_BDC_UPGRADE:
  276. case State::NONE:
  277. default:
  278. {
  279. // do nothing
  280. break;
  281. }
  282. }
  283. return aviID;
  284. }
  285. bool
  286. ConfirmationPage::OnWizNext()
  287. {
  288. LOG_FUNCTION(ConfirmationPage::OnWizNext);
  289. State& state = State::GetInstance();
  290. DoOperation(hwnd, PromoteThreadProc, DetermineAnimation());
  291. if (state.GetNeedsReboot())
  292. {
  293. Win::PropSheet_RebootSystem(Win::GetParent(hwnd));
  294. }
  295. int nextPage = IDD_FAILURE;
  296. if (!state.IsOperationRetryAllowed())
  297. {
  298. nextPage = IDD_FINISH;
  299. }
  300. GetWizard().SetNextPageID(hwnd, nextPage);
  301. return true;
  302. }
  303. // noRoleMessageResId - resource ID of the string to use to format messages
  304. // when no role change error message is available.
  305. //
  306. // roleMessageResId - resource ID of the string to use to format messages when
  307. // a role change error message is available.
  308. //
  309. // "is available" => an operation results message has been set on the global
  310. // state object.
  311. String
  312. ComposeFailureMessageHelper(
  313. const Win::Error& error,
  314. unsigned noRoleMessageResId,
  315. unsigned roleMessageResId)
  316. {
  317. State& state = State::GetInstance();
  318. String win32_message = error.GetMessage();
  319. String opMessage = state.GetOperationResultsMessage();
  320. String message;
  321. if (
  322. error.GetHresult() == Win32ToHresult(ERROR_DS_CANT_ON_NON_LEAF)
  323. && state.GetOperation() == State::DEMOTE)
  324. {
  325. // supercede the meaningless error text for this situation.
  326. win32_message = String::load(IDS_DEMOTE_DOMAIN_HAS_DEPENDENTS);
  327. }
  328. if (error.GetHresult() == Win32ToHresult(ERROR_CANCELLED))
  329. {
  330. // this message may be a failure message from the operation that was
  331. // taking place when the cancel request was received. In that case,
  332. // since the cancel has occurred, we don't care about this message
  333. opMessage.erase();
  334. }
  335. if (error.GetHresult() == Win32ToHresult(ERROR_BAD_NETPATH))
  336. {
  337. // n27117
  338. win32_message = String::load(IDS_RAS_BAD_NETPATH);
  339. }
  340. if (opMessage.empty())
  341. {
  342. message =
  343. String::format(
  344. noRoleMessageResId,
  345. win32_message.c_str());
  346. }
  347. else
  348. {
  349. message =
  350. String::format(
  351. roleMessageResId,
  352. win32_message.c_str(),
  353. opMessage.c_str());
  354. }
  355. return message;
  356. }
  357. void
  358. ComposeFailureMessage(
  359. const Win::Error& error,
  360. bool wasDisjoined,
  361. const String& originalDomainName)
  362. {
  363. LOG_FUNCTION(ComposeFailureMessage);
  364. String message =
  365. ComposeFailureMessageHelper(
  366. error,
  367. IDS_OPERATION_FAILED_NO_RESULT_MESSAGE,
  368. IDS_OPERATION_FAILED);
  369. if (wasDisjoined)
  370. {
  371. message += String::format(IDS_DISJOINED, originalDomainName.c_str());
  372. }
  373. State& state = State::GetInstance();
  374. if (
  375. state.GetOperationResultsFlags()
  376. & DSROLE_IFM_RESTORED_DATABASE_FILES_MOVED)
  377. {
  378. message += L"\r\n\r\n" + String::load(IDS_MUST_RESTORE_IFM_FILES_AGAIN);
  379. }
  380. state.SetFailureMessage(message);
  381. }
  382. String
  383. GetSbsLimitMessage()
  384. {
  385. LOG_FUNCTION(GetSbsLimitMessage);
  386. static const String SBSLIMIT_DLL(L"sbslimit.dll");
  387. String message;
  388. HMODULE sbsDll = 0;
  389. HRESULT hr =
  390. Win::LoadLibraryEx(SBSLIMIT_DLL, LOAD_LIBRARY_AS_DATAFILE, sbsDll);
  391. if (FAILED(hr))
  392. {
  393. LOG(L"Unable to load SBSLIMIT_DLL");
  394. // fall back to a message of our own
  395. message = String::load(IDS_SBS_LIMITATION_MESSAGE);
  396. }
  397. else
  398. {
  399. // string 3 is the dcpromo message
  400. message = Win::LoadString(3, sbsDll);
  401. HRESULT unused = Win::FreeLibrary(sbsDll);
  402. ASSERT(SUCCEEDED(unused));
  403. }
  404. return message;
  405. }
  406. // Check if this is a Small Business Server product; if so, present throw an
  407. // error, as SBS should only allow new forest & demote. Rather brutal to do
  408. // it in this fashion, but SBS users should not be using dcpromo directly.
  409. // 353854, 353856
  410. void
  411. CheckSmallBusinessServerLimitations(HWND hwnd)
  412. throw (DS::Error)
  413. {
  414. LOG_FUNCTION(CheckSmallBusinessServerLimitations);
  415. ASSERT(Win::IsWindow(hwnd));
  416. State& state = State::GetInstance();
  417. State::Operation op = state.GetOperation();
  418. switch (op)
  419. {
  420. case State::TREE:
  421. case State::CHILD:
  422. case State::REPLICA:
  423. {
  424. // Tree and child operations are not allowed with the SBS product.
  425. // Replica is allowed, if it is of a forest root domain.
  426. OSVERSIONINFOEX info;
  427. HRESULT hr = Win::GetVersionEx(info);
  428. BREAK_ON_FAILED_HRESULT(hr);
  429. if (info.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED)
  430. {
  431. if (op == State::REPLICA)
  432. {
  433. String domain = state.GetReplicaDomainDNSName();
  434. // Since domain has been previously validated by calling
  435. // DsGetDcName, we don't anticipate that GetForestName will
  436. // have any difficulty.
  437. hr = S_OK;
  438. String forest = GetForestName(domain, &hr);
  439. if (FAILED(hr))
  440. {
  441. ShowDcNotFoundErrorDialog(
  442. hwnd,
  443. -1,
  444. domain,
  445. String::load(IDS_WIZARD_TITLE),
  446. String::format(IDS_DC_NOT_FOUND, domain.c_str()),
  447. false);
  448. throw
  449. DS::Error(
  450. hr,
  451. String::format(
  452. IDS_UNABLE_TO_DETERMINE_FOREST,
  453. domain.c_str()),
  454. String::load(IDS_WIZARD_TITLE));
  455. }
  456. DNS_RELATE_STATUS compare = Dns::CompareNames(domain, forest);
  457. if (compare == DnsNameCompareEqual)
  458. {
  459. LOG(L"replica is of forest root, allowing promote");
  460. break;
  461. }
  462. }
  463. // This machine is an SBS machine under restricted license.
  464. // Extract an error message from an SBS dll.
  465. LOG(L"Is SBS Restricted");
  466. String message = GetSbsLimitMessage();
  467. // do not call state.SetOperationResultsMessage with this
  468. // message, rather, include it in the thrown error.
  469. throw
  470. DS::Error(
  471. S_OK, // don't trigger cred retry
  472. message,
  473. String::load(IDS_SMALL_BUSINESS_LIMIT));
  474. }
  475. break;
  476. }
  477. case State::DEMOTE:
  478. case State::FOREST:
  479. case State::ABORT_BDC_UPGRADE:
  480. case State::NONE:
  481. default:
  482. {
  483. // do nothing
  484. break;
  485. }
  486. }
  487. }
  488. // Picks the name of a domain controller suitable for the creation of a
  489. // replica. Since a server must be a member of the domain before it can be
  490. // made a replica of that domain, the server may also be joined to the domain
  491. // before the replica operation is attempted.
  492. //
  493. // We need to ensure that the domain controller used to join the domain is the
  494. // same domain controller used to replicate the domain. Also, since a machine
  495. // account for the server may already exist on one or more -- but not
  496. // necessarily all -- domain controllers, we need to pick a domain controller
  497. // that has that machine account. 406462
  498. //
  499. // domainName - DNS domain name of domain for which a replica is to be found.
  500. //
  501. // resultDcName - receives the located name, or the empty string on failure.
  502. HRESULT
  503. GetJoinAndReplicaDcName(const String& domainName, String& resultDcName)
  504. {
  505. LOG_FUNCTION(GetJoinAndReplicaDcName);
  506. ASSERT(!domainName.empty());
  507. resultDcName.erase();
  508. HRESULT hr = S_OK;
  509. do
  510. {
  511. // determine the local computer's domain machine account name. This is the
  512. // name of the local computer, plus a "$"
  513. String netbiosName = Win::GetComputerNameEx(ComputerNameNetBIOS);
  514. String accountName = netbiosName + L"$";
  515. LOG(accountName);
  516. // look for a domain controller that has a machine account for the local
  517. // computer. Not all domain controllers may have this account, due to
  518. // replication latency.
  519. DOMAIN_CONTROLLER_INFO* info = 0;
  520. hr =
  521. MyDsGetDcNameWithAccount(
  522. 0,
  523. accountName,
  524. UF_WORKSTATION_TRUST_ACCOUNT | UF_SERVER_TRUST_ACCOUNT,
  525. domainName,
  526. DS_DIRECTORY_SERVICE_REQUIRED | DS_FORCE_REDISCOVERY,
  527. info);
  528. BREAK_ON_FAILED_HRESULT(hr);
  529. ASSERT(info->DomainControllerName);
  530. if (info->DomainControllerName)
  531. {
  532. resultDcName =
  533. Computer::RemoveLeadingBackslashes(info->DomainControllerName);
  534. LOG(resultDcName);
  535. }
  536. ::NetApiBufferFree(info);
  537. if (!resultDcName.empty())
  538. {
  539. return hr;
  540. }
  541. }
  542. while (0);
  543. // either there is no domain controller reachable with the required
  544. // account, or the account does not exist, or DsGetDcName returned an
  545. // empty name
  546. LOG(L"Falling back to non-account DsGetDcName");
  547. return GetDcName(domainName, resultDcName);
  548. }
  549. void
  550. EvaluateRoleChangeState()
  551. throw (DS::Error)
  552. {
  553. LOG_FUNCTION(EvaluateRoleChangeState);
  554. int messageResId = 0;
  555. DSROLE_OPERATION_STATE opState = ::DsRoleOperationIdle;
  556. DSROLE_OPERATION_STATE_INFO* info = 0;
  557. HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
  558. if (SUCCEEDED(hr) && info)
  559. {
  560. opState = info->OperationState;
  561. ::DsRoleFreeMemory(info);
  562. }
  563. else
  564. {
  565. throw
  566. DS::Error(
  567. hr,
  568. String::load(IDS_UNABLE_TO_DETERMINE_OP_STATE),
  569. String::load(IDS_WIZARD_TITLE));
  570. }
  571. switch (opState)
  572. {
  573. case ::DsRoleOperationIdle:
  574. {
  575. // do nothing
  576. break;
  577. }
  578. case ::DsRoleOperationActive:
  579. {
  580. // a role change operation is underway
  581. messageResId = IDS_ROLE_CHANGE_IN_PROGRESS;
  582. break;
  583. }
  584. case ::DsRoleOperationNeedReboot:
  585. {
  586. // a role change has already taken place, need to reboot before
  587. // attempting another.
  588. messageResId = IDS_ROLE_CHANGE_NEEDS_REBOOT;
  589. break;
  590. }
  591. default:
  592. {
  593. ASSERT(false);
  594. break;
  595. }
  596. }
  597. if (messageResId)
  598. {
  599. throw
  600. DS::Error(
  601. S_OK,
  602. String::load(messageResId),
  603. String::load(IDS_WIZARD_TITLE));
  604. }
  605. }
  606. // Verify that the current role of the machine is correct for the type of
  607. // operation we're about to attempt. Throw an exception if it is not.
  608. void
  609. DoubleCheckRoleChangeState()
  610. throw (DS::Error)
  611. {
  612. LOG_FUNCTION(DoubleCheckRoleChangeState);
  613. // Make sure that an operation is not in progress or pending reboot.
  614. EvaluateRoleChangeState();
  615. State& state = State::GetInstance();
  616. Computer& computer = state.GetComputer();
  617. HRESULT hr = computer.Refresh();
  618. if (FAILED(hr))
  619. {
  620. throw
  621. DS::Error(
  622. hr,
  623. String::load(IDS_UNABLE_TO_DETERMINE_COMPUTER_CONFIG),
  624. String::load(IDS_WIZARD_TITLE));
  625. }
  626. switch (state.GetOperation())
  627. {
  628. case State::TREE:
  629. case State::CHILD:
  630. case State::REPLICA:
  631. case State::FOREST:
  632. case State::ABORT_BDC_UPGRADE:
  633. {
  634. // Make sure the machine is not already a DC. If the machine is
  635. // an NT4 DC finishing upgrade, then its role will be member
  636. // server, not domain controller.
  637. if (computer.IsDomainController())
  638. {
  639. throw
  640. DS::Error(
  641. S_OK,
  642. String::load(IDS_MACHINE_IS_ALREADY_DC),
  643. String::load(IDS_WIZARD_TITLE));
  644. }
  645. break;
  646. }
  647. case State::DEMOTE:
  648. {
  649. // Make sure the machine is still a DC
  650. if (!computer.IsDomainController())
  651. {
  652. throw
  653. DS::Error(
  654. S_OK,
  655. String::load(IDS_MACHINE_IS_NOT_ALREADY_DC),
  656. String::load(IDS_WIZARD_TITLE));
  657. }
  658. break;
  659. }
  660. case State::NONE:
  661. default:
  662. {
  663. ASSERT(false);
  664. // do nothing
  665. break;
  666. }
  667. }
  668. }
  669. // thread launched by ProgressDialog::OnInit.
  670. // CODEWORK: Man, this function has evolved into a real mess.
  671. void
  672. PromoteThreadProc(ProgressDialog& progress)
  673. {
  674. LOG_FUNCTION(PromoteThreadProc);
  675. //
  676. // Access to members of ProgressDialog is not, by default, threadsafe.
  677. // However, since the only members we access are atomic data types, this
  678. // is not a problem. Note also that calls to ProgressDialog Update
  679. // methods usually resolve to calls to SendMessage on UI elements of the
  680. // dialog. This too is threadsafe, as SendMessage is always executed
  681. // in the thread that created the window (tho it may block the calling
  682. // thread).
  683. //
  684. UINT message = ProgressDialog::THREAD_SUCCEEDED;
  685. bool retry = false;
  686. bool wasDisjoined = false;
  687. State& state = State::GetInstance();
  688. String originalDomainName;
  689. // a reference, as we will refresh the object
  690. Computer& computer = state.GetComputer();
  691. State::RunContext context = state.GetRunContext();
  692. do
  693. {
  694. LOG(L"top of retry loop");
  695. DisableConsoleLocking();
  696. // clear the state of the operation attempt
  697. bool exceptionWasThrown = false;
  698. Win::Error errorThrown(0, 0);
  699. message = ProgressDialog::THREAD_SUCCEEDED;
  700. retry = false;
  701. state.SetOperationResultsMessage(String());
  702. state.SetOperationResultsFlags(0);
  703. progress.UpdateText(IDS_STARTING);
  704. try
  705. {
  706. CheckSmallBusinessServerLimitations(progress.GetHWND());
  707. // Double check that the role of the machine is still ok for the
  708. // operation to proceed. This is mostly a paranoid check, but there
  709. // have been cases during development where the promotion actually
  710. // succeeded, but reported a failure, and attempting the operation
  711. // again trashes the DS. Such problems indicate the presence of
  712. // other serious bugs, but if we can cheaply avoid zorching a DC,
  713. // then bully for us.
  714. // NTRAID#NTBUG9-345115-2001/03/23-sburns
  715. DoubleCheckRoleChangeState();
  716. switch (state.GetOperation())
  717. {
  718. case State::REPLICA:
  719. {
  720. // if we're using an answerfile, look for a replication partner
  721. // there. 107143
  722. String replDc;
  723. if (state.UsingAnswerFile())
  724. {
  725. replDc =
  726. state.GetAnswerFileOption(
  727. AnswerFile::OPTION_REPLICATION_SOURCE);
  728. state.SetReplicationPartnerDC(replDc);
  729. }
  730. if (context != State::BDC_UPGRADE)
  731. {
  732. String replicaDnsDomainName =
  733. state.GetReplicaDomainDNSName();
  734. if (!computer.IsJoinedToDomain(replicaDnsDomainName) )
  735. {
  736. // need to join the domain we will replicate. Determine
  737. // the name of a domain controller to use for join and
  738. // replication. 270233
  739. if (replDc.empty())
  740. {
  741. // answerfile did not specify a dc. So pick one
  742. // ourselves.
  743. HRESULT hr =
  744. GetJoinAndReplicaDcName(
  745. replicaDnsDomainName,
  746. replDc);
  747. if (FAILED(hr))
  748. {
  749. throw
  750. DS::Error(
  751. hr,
  752. IDS_JOIN_DOMAIN_FAILED);
  753. }
  754. state.SetReplicationPartnerDC(replDc);
  755. }
  756. if (computer.IsJoinedToDomain())
  757. {
  758. originalDomainName =
  759. computer.GetDomainNetbiosName();
  760. }
  761. progress.UpdateText(IDS_CHANGING_DOMAIN);
  762. // this will unjoin if necessary
  763. DS::JoinDomain(
  764. replicaDnsDomainName,
  765. replDc,
  766. state.GetUsername(),
  767. state.GetPassword(),
  768. state.GetUserDomainName());
  769. if (ComputerWasRenamedAndNeedsReboot())
  770. {
  771. // If we make it to this point, the machine was joined
  772. // to a domain, and the name changed as a side-effect,
  773. // and will need to be rebooted even if the promote
  774. // fails. Set a flag to note that fact.
  775. // NTRAID#NTBUG9-346120-2001/04/04-sburns
  776. state.SetNeedsReboot();
  777. }
  778. HRESULT hr = computer.Refresh();
  779. ASSERT(SUCCEEDED(hr));
  780. if (!originalDomainName.empty())
  781. {
  782. wasDisjoined = true;
  783. }
  784. }
  785. DS::CreateReplica(progress);
  786. }
  787. else
  788. {
  789. DS::UpgradeBDC(progress);
  790. }
  791. break;
  792. }
  793. case State::FOREST:
  794. case State::TREE:
  795. case State::CHILD:
  796. {
  797. if (context != State::PDC_UPGRADE)
  798. {
  799. if (computer.IsJoinedToDomain())
  800. {
  801. // need to unjoin the domain we belong to
  802. originalDomainName = computer.GetDomainNetbiosName();
  803. ASSERT(!originalDomainName.empty());
  804. progress.UpdateText(
  805. String::format(IDS_DISJOINING_PROGRESS,
  806. originalDomainName.c_str()));
  807. if (!DS::DisjoinDomain())
  808. {
  809. // the computer account was not removed.
  810. if (!state.RunHiddenUnattended())
  811. {
  812. popup.Info(
  813. progress.GetHWND(),
  814. String::load(IDS_COULDNT_REMOVE_COMPUTER_ACCOUNT_TEXT));
  815. }
  816. }
  817. if (ComputerWasRenamedAndNeedsReboot())
  818. {
  819. // If we make it to this point, the machine was
  820. // disjoined from a domain, and the name changed as a
  821. // side-effect, and will need to be rebooted even if
  822. // the promote fails. Set a flag to note that fact.
  823. // NTRAID#NTBUG9-346120-2001/04/04-sburns
  824. state.SetNeedsReboot();
  825. }
  826. HRESULT hr = computer.Refresh();
  827. ASSERT(SUCCEEDED(hr));
  828. wasDisjoined = true;
  829. }
  830. DS::CreateNewDomain(progress);
  831. }
  832. else
  833. {
  834. DS::UpgradePDC(progress);
  835. }
  836. break;
  837. }
  838. case State::ABORT_BDC_UPGRADE:
  839. {
  840. ASSERT(state.GetRunContext() == State::BDC_UPGRADE);
  841. DS::AbortBDCUpgrade();
  842. break;
  843. }
  844. case State::DEMOTE:
  845. {
  846. DS::DemoteDC(progress);
  847. break;
  848. }
  849. case State::NONE:
  850. default:
  851. {
  852. ASSERT(false);
  853. message = ProgressDialog::THREAD_FAILED;
  854. }
  855. }
  856. //
  857. // At this point, the operation was successfully completed.
  858. //
  859. DoPostOperationStuff(progress);
  860. state.SetOperationResults(State::SUCCESS);
  861. state.SetNeedsReboot();
  862. }
  863. catch (const Win::Error& err)
  864. {
  865. LOG(L"Exception caught");
  866. exceptionWasThrown = true;
  867. errorThrown = err;
  868. LOG(L"catch completed");
  869. }
  870. if (exceptionWasThrown)
  871. {
  872. LOG(L"handling exception");
  873. // go interactive from now on
  874. state.ClearHiddenWhileUnattended(); // 22935
  875. if (
  876. state.GetRunContext() != State::PDC_UPGRADE
  877. && state.GetRunContext() != State::BDC_UPGRADE)
  878. {
  879. // re-enable console locking if not a downlevel upgrade 28496
  880. EnableConsoleLocking();
  881. }
  882. state.SetOperationResults(State::FAILURE);
  883. progress.UpdateText(String());
  884. message = ProgressDialog::THREAD_FAILED;
  885. HRESULT errorThrownHresult = errorThrown.GetHresult();
  886. if (!state.IsOperationRetryAllowed())
  887. {
  888. // The operation failure was such that the user should not be
  889. // allowed to retry it. In this case, we skip our special-case
  890. // handling of known failure codes (as expressed by the other else
  891. // if clauses here), and just report the failure.
  892. //
  893. // NTRAID#NTBUG9-296872-2001/01/29-sburns
  894. retry = false;
  895. }
  896. else if (
  897. errorThrownHresult == Win32ToHresult(ERROR_ACCESS_DENIED)
  898. || errorThrownHresult == Win32ToHresult(ERROR_LOGON_FAILURE)
  899. || errorThrownHresult == Win32ToHresult(ERROR_NOT_AUTHENTICATED)
  900. || errorThrownHresult == Win32ToHresult(RPC_S_SEC_PKG_ERROR)
  901. || errorThrownHresult == Win32ToHresult(ERROR_DS_DRA_ACCESS_DENIED)
  902. || errorThrownHresult == Win32ToHresult(ERROR_INVALID_PASSWORD)
  903. || errorThrownHresult == Win32ToHresult(ERROR_PASSWORD_EXPIRED)
  904. || errorThrownHresult == Win32ToHresult(ERROR_ACCOUNT_DISABLED)
  905. || errorThrownHresult == Win32ToHresult(ERROR_ACCOUNT_LOCKED_OUT) )
  906. {
  907. // bad credentials. ask for new ones
  908. String failureMessage =
  909. ComposeFailureMessageHelper(
  910. errorThrown,
  911. IDS_OPERATION_FAILED_GET_CRED_NO_RESULT,
  912. IDS_OPERATION_FAILED_GET_CRED);
  913. GetCredentialsDialog dlg(failureMessage);
  914. if (dlg.ModalExecute(progress) == IDOK)
  915. {
  916. retry = true;
  917. // jump to top of operation loop
  918. continue;
  919. }
  920. LOG(L"credential retry canceled");
  921. ComposeFailureMessage(
  922. errorThrown,
  923. wasDisjoined,
  924. originalDomainName);
  925. break;
  926. }
  927. else if (errorThrownHresult == Win32ToHresult(ERROR_DOMAIN_EXISTS))
  928. {
  929. LOG(L"domain exists: prompting for re-install");
  930. // ask if the user wishes to reinstall the domain.
  931. if (
  932. popup.MessageBox(
  933. progress.GetHWND(),
  934. String::format(
  935. IDS_REINSTALL_DOMAIN_MESSAGE,
  936. state.GetNewDomainDNSName().c_str()),
  937. MB_YESNO | MB_ICONWARNING) == IDYES)
  938. {
  939. state.SetDomainReinstallFlag(true);
  940. retry = true;
  941. // jump to top of operation loop
  942. continue;
  943. }
  944. LOG(L"reinstall domain retry canceled");
  945. }
  946. else if (
  947. errorThrownHresult ==
  948. Win32ToHresult(ERROR_DOMAIN_CONTROLLER_EXISTS))
  949. {
  950. LOG(L"domain controller exists: prompting to force promote");
  951. // ask if the user wants to re-install the domain controller
  952. if (
  953. popup.MessageBox(
  954. progress.GetHWND(),
  955. String::format(
  956. IDS_REINSTALL_DOMAIN_CONTROLLER_MESSAGE,
  957. state.GetComputer().GetNetbiosName().c_str()),
  958. MB_YESNO | MB_ICONWARNING) == IDYES)
  959. {
  960. state.SetDomainControllerReinstallFlag(true);
  961. retry = true;
  962. // jump to the top of the operation loop
  963. continue;
  964. }
  965. LOG(L"reinstall domain controller retry canceled");
  966. }
  967. // if we're retrying, then we should have jumped to the top of
  968. // the loop.
  969. ASSERT(!retry);
  970. ComposeFailureMessage(
  971. errorThrown,
  972. wasDisjoined,
  973. originalDomainName);
  974. // NTRAID#NTBUG9-416968-2001/06/14-sburns
  975. #ifdef DBG
  976. if (state.IsExitOnFailureMode())
  977. {
  978. // Farewell.
  979. LOG(L"exit-on-failure mode active: time to die.");
  980. ::ExitProcess(0);
  981. }
  982. #endif
  983. Win::MessageBox(
  984. progress.GetHWND(),
  985. state.GetFailureMessage(),
  986. errorThrown.GetSummary(), // title the error was built with
  987. MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
  988. }
  989. }
  990. while (retry);
  991. #ifdef DBG
  992. if (message == ProgressDialog::THREAD_FAILED)
  993. {
  994. ASSERT(state.GetOperationResultsCode() == State::FAILURE);
  995. }
  996. else
  997. {
  998. ASSERT(state.GetOperationResultsCode() == State::SUCCESS);
  999. }
  1000. #endif
  1001. LOG(L"posting message to progress window");
  1002. HRESULT hr = Win::PostMessage(progress.GetHWND(), message, 0, 0);
  1003. ASSERT(SUCCEEDED(hr));
  1004. // do not call _endthread here, or stack will not be properly cleaned up
  1005. }
  1006. bool
  1007. ConfirmationPage::OnCommand(
  1008. HWND windowFrom,
  1009. unsigned controlIdFrom,
  1010. unsigned code)
  1011. {
  1012. bool result = false;
  1013. switch (controlIdFrom)
  1014. {
  1015. case IDCANCEL:
  1016. {
  1017. // multi-line edit control eats escape keys. This is a workaround
  1018. // from ericb, to forward the message to the prop sheet.
  1019. Win::SendMessage(
  1020. Win::GetParent(hwnd),
  1021. WM_COMMAND,
  1022. MAKEWPARAM(controlIdFrom, code),
  1023. (LPARAM) windowFrom);
  1024. break;
  1025. }
  1026. case IDC_MESSAGE:
  1027. {
  1028. switch (code)
  1029. {
  1030. case EN_SETFOCUS:
  1031. {
  1032. if (needToKillSelection)
  1033. {
  1034. // kill the text selection
  1035. Win::Edit_SetSel(windowFrom, -1, -1);
  1036. needToKillSelection = false;
  1037. result = true;
  1038. }
  1039. break;
  1040. }
  1041. case MultiLineEditBoxThatForwardsEnterKey::FORWARDED_ENTER:
  1042. {
  1043. // our subclasses mutli-line edit control will send us
  1044. // WM_COMMAND messages when the enter key is pressed. We
  1045. // reinterpret this message as a press on the default button of
  1046. // the prop sheet.
  1047. // This workaround from phellyar.
  1048. // NTRAID#NTBUG9-232092-2000/11/22-sburns
  1049. HWND propSheet = Win::GetParent(hwnd);
  1050. int defaultButtonId =
  1051. Win::Dialog_GetDefaultButtonId(propSheet);
  1052. // we expect that there is always a default button on the prop sheet
  1053. ASSERT(defaultButtonId);
  1054. Win::SendMessage(
  1055. propSheet,
  1056. WM_COMMAND,
  1057. MAKELONG(defaultButtonId, BN_CLICKED),
  1058. 0);
  1059. result = true;
  1060. break;
  1061. }
  1062. }
  1063. break;
  1064. }
  1065. default:
  1066. {
  1067. // do nothing
  1068. break;
  1069. }
  1070. }
  1071. return result;
  1072. }