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.

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