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.

758 lines
18 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // Id changes dialog
  4. //
  5. // 3-10-98 sburns
  6. #include "headers.hxx"
  7. #include "iddlg.hpp"
  8. #include "moredlg.hpp"
  9. #include "resource.h"
  10. #include "helpids.h"
  11. #include "state.hpp"
  12. #include <DiagnoseDcNotFound.hpp>
  13. #include <DiagnoseDcNotFound.h>
  14. static const DWORD HELP_MAP[] =
  15. {
  16. IDC_MESSAGE, NO_HELP,
  17. IDC_FULL_NAME, IDH_IDENT_CHANGES_PREVIEW_NAME,
  18. IDC_FULL_LABEL, IDH_IDENT_CHANGES_PREVIEW_NAME,
  19. IDC_NEW_NAME, IDH_IDENT_CHANGES_NEW_NAME,
  20. IDC_MORE, IDH_IDENT_CHANGES_MORE_BUTTON,
  21. IDC_DOMAIN_BUTTON, IDH_IDENT_CHANGES_MEMBER_DOMAIN,
  22. IDC_WORKGROUP_BUTTON, IDH_IDENT_CHANGES_MEMBER_WORKGRP,
  23. IDC_DOMAIN, IDH_IDENT_CHANGES_MEMBER_DOMAIN_TEXTBOX,
  24. IDC_WORKGROUP, IDH_IDENT_CHANGES_MEMBER_WORKGRP_TEXTBOX,
  25. IDC_FIND, NO_HELP,
  26. IDC_GROUP, NO_HELP,
  27. 0, 0
  28. };
  29. IDChangesDialog::IDChangesDialog(bool isPersonal)
  30. :
  31. Dialog((isPersonal) ? IDD_CHANGES_PER : IDD_CHANGES, HELP_MAP),
  32. isInitializing(false),
  33. fIsPersonal(isPersonal)
  34. {
  35. LOG_CTOR(IDChangesDialog);
  36. }
  37. IDChangesDialog::~IDChangesDialog()
  38. {
  39. LOG_DTOR(IDChangesDialog);
  40. }
  41. void
  42. IDChangesDialog::enable(HWND hwnd)
  43. {
  44. State& state = State::GetInstance();
  45. bool networkingInstalled = state.IsNetworkingInstalled();
  46. bool isDc = state.IsMachineDc();
  47. bool workgroup = Win::IsDlgButtonChecked(hwnd, IDC_WORKGROUP_BUTTON);
  48. // Note that this can be called via EN_CHANGE before OnInit, so that
  49. // the workgroup checkbox may not have been properly enabled.
  50. Win::EnableWindow(
  51. Win::GetDlgItem(hwnd, IDC_DOMAIN),
  52. !workgroup && networkingInstalled && !isDc);
  53. Win::EnableWindow(
  54. Win::GetDlgItem(hwnd, IDC_FIND),
  55. !workgroup && networkingInstalled && !isDc);
  56. Win::EnableWindow(
  57. Win::GetDlgItem(hwnd, IDC_WORKGROUP),
  58. workgroup && networkingInstalled && !isDc);
  59. Win::EnableWindow(
  60. Win::GetDlgItem(hwnd, IDC_WORKGROUP_BUTTON),
  61. networkingInstalled && !isDc);
  62. Win::EnableWindow(
  63. Win::GetDlgItem(hwnd, IDC_DOMAIN_BUTTON),
  64. networkingInstalled && !isDc);
  65. Win::EnableWindow(
  66. Win::GetDlgItem(hwnd, IDC_GROUP),
  67. networkingInstalled && !isDc);
  68. bool b = false;
  69. if (workgroup)
  70. {
  71. b = !Win::GetTrimmedDlgItemText(hwnd, IDC_WORKGROUP).empty();
  72. }
  73. else
  74. {
  75. b = !Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty();
  76. }
  77. bool enabled =
  78. state.ChangesNeedSaving()
  79. && b
  80. && !Win::GetTrimmedDlgItemText(hwnd, IDC_NEW_NAME).empty();
  81. Win::EnableWindow(Win::GetDlgItem(hwnd, IDOK), enabled);
  82. }
  83. void
  84. showAndEnableWindow(HWND parent, int resID, int show)
  85. {
  86. ASSERT(Win::IsWindow(parent));
  87. ASSERT(resID > 0);
  88. ASSERT(show == SW_SHOW || show == SW_HIDE);
  89. HWND window = Win::GetDlgItem(parent, resID);
  90. Win::ShowWindow(window, show);
  91. Win::EnableWindow(window, show == SW_SHOW ? true : false);
  92. }
  93. void
  94. SetUppercaseStyle(HWND edit)
  95. {
  96. LOG_FUNCTION(SetUppercaseStyle);
  97. ASSERT(Win::IsWindow(edit));
  98. LONG style = 0;
  99. HRESULT hr = Win::GetWindowLong(edit, GWL_STYLE, style);
  100. ASSERT(SUCCEEDED(hr));
  101. style |= ES_UPPERCASE;
  102. hr = Win::SetWindowLong(edit, GWL_STYLE, style);
  103. ASSERT(SUCCEEDED(hr));
  104. }
  105. void
  106. IDChangesDialog::OnInit()
  107. {
  108. LOG_FUNCTION(IDChangesDialog::OnInit);
  109. isInitializing = true;
  110. State& state = State::GetInstance();
  111. Win::SetDlgItemText(hwnd, IDC_FULL_NAME, state.GetFullComputerName());
  112. Win::SetDlgItemText(hwnd, IDC_NEW_NAME, state.GetShortComputerName());
  113. bool joinedToWorkgroup = state.IsMemberOfWorkgroup();
  114. ASSERT( joinedToWorkgroup || !fIsPersonal );
  115. Win::CheckDlgButton(
  116. hwnd,
  117. IDC_WORKGROUP_BUTTON,
  118. joinedToWorkgroup ? BST_CHECKED : BST_UNCHECKED);
  119. Win::CheckDlgButton(
  120. hwnd,
  121. IDC_DOMAIN_BUTTON,
  122. joinedToWorkgroup ? BST_UNCHECKED : BST_CHECKED);
  123. Win::SetDlgItemText(
  124. hwnd,
  125. joinedToWorkgroup ? IDC_WORKGROUP : IDC_DOMAIN,
  126. state.GetDomainName());
  127. bool networkingInstalled = state.IsNetworkingInstalled();
  128. bool tcpInstalled = networkingInstalled && IsTcpIpInstalled();
  129. int show = tcpInstalled ? SW_SHOW : SW_HIDE;
  130. showAndEnableWindow(hwnd, IDC_FULL_LABEL, show);
  131. showAndEnableWindow(hwnd, IDC_FULL_NAME, show);
  132. showAndEnableWindow(hwnd, IDC_MORE, show);
  133. HWND newNameEdit = Win::GetDlgItem(hwnd, IDC_NEW_NAME);
  134. HWND domainNameEdit = Win::GetDlgItem(hwnd, IDC_DOMAIN);
  135. Win::Edit_LimitText(
  136. domainNameEdit,
  137. tcpInstalled ? Dns::MAX_NAME_LENGTH : DNLEN);
  138. Win::Edit_LimitText(
  139. newNameEdit,
  140. tcpInstalled ? Dns::MAX_LABEL_LENGTH : MAX_COMPUTERNAME_LENGTH);
  141. if (!tcpInstalled)
  142. {
  143. // Without tcp/ip, new name and domain need to look like netbios, so set
  144. // the uppercase style on those boxen.
  145. SetUppercaseStyle(newNameEdit);
  146. SetUppercaseStyle(domainNameEdit);
  147. }
  148. Win::Edit_LimitText(Win::GetDlgItem(hwnd, IDC_WORKGROUP), DNLEN);
  149. // no networking at all further restricts the UI to just NetBIOS-like
  150. // computer name changes.
  151. if (!networkingInstalled)
  152. {
  153. // enable() will handle disabling the inapplicable ui
  154. Win::SetWindowText(
  155. Win::GetDlgItem(hwnd, IDC_MESSAGE),
  156. String::load(IDS_NAME_MESSAGE));
  157. }
  158. else
  159. {
  160. Win::SetWindowText(
  161. Win::GetDlgItem(hwnd, IDC_MESSAGE),
  162. String::load(IDS_NAME_AND_MEMBERSHIP_MESSAGE));
  163. }
  164. ClearChanges();
  165. enable(hwnd);
  166. isInitializing = false;
  167. }
  168. bool
  169. ValidateNetbiosAndFullNames(HWND dialog, const String& shortName)
  170. {
  171. LOG_FUNCTION(ValidateNetbiosAndFullNames);
  172. ASSERT(Win::IsWindow(dialog));
  173. ASSERT(!shortName.empty());
  174. HRESULT err = S_OK;
  175. String flatname = Dns::HostnameToNetbiosName(shortName, &err);
  176. if (flatname.empty() || FAILED(err))
  177. {
  178. // the flatname conversion failed.
  179. popup.Gripe(
  180. dialog,
  181. IDC_NEW_NAME,
  182. err,
  183. String::format(
  184. IDS_CONVERSION_TO_NETBIOS_NAME_FAILED,
  185. shortName.c_str()));
  186. return false;
  187. }
  188. if (flatname.is_numeric())
  189. {
  190. // the truncated version may be a number. If we catch this here, it is
  191. // because the name was truncated.. This is because the hostname is
  192. // checked against being a number in ValidateComputerNames. 401076
  193. ASSERT(shortName.length() > flatname.length());
  194. popup.Gripe(
  195. dialog,
  196. IDC_NEW_NAME,
  197. String::format(
  198. IDS_NETBIOS_NAME_NUMERIC,
  199. flatname.c_str(),
  200. CNLEN));
  201. return false;
  202. }
  203. if (shortName.length() > flatname.length())
  204. {
  205. // warn that the netbios name will be truncated.
  206. popup.Info(
  207. dialog,
  208. String::format(
  209. IDS_NAME_TRUNCATED,
  210. CNLEN,
  211. flatname.c_str()));
  212. }
  213. // here the flatname is of the proper length in bytes (because the
  214. // hostname-to-flatname API guarantees that), is not a number, so the only
  215. // other possible syntax problem would be illegal characters.
  216. if (ValidateNetbiosComputerName(flatname) != VALID_NAME)
  217. {
  218. popup.Gripe(
  219. dialog,
  220. IDC_NEW_NAME,
  221. String::format(
  222. IDS_BAD_NETBIOS_CHARACTERS,
  223. flatname.c_str()));
  224. return false;
  225. }
  226. State& state = State::GetInstance();
  227. if (!state.IsNetworkingInstalled())
  228. {
  229. // We can't validate these names further without networking, so they
  230. // pass
  231. return true;
  232. }
  233. if (state.WasNetbiosComputerNameChanged())
  234. {
  235. HRESULT hr = MyNetValidateName(flatname, NetSetupMachine);
  236. if (FAILED(hr))
  237. {
  238. // the netbios name is in use
  239. popup.Gripe(
  240. dialog,
  241. IDC_NEW_NAME,
  242. hr,
  243. String::format(IDS_VALIDATE_NAME_FAILED, flatname.c_str()));
  244. return false;
  245. }
  246. }
  247. // here, the netbios name has not changed with the new short name, or is
  248. // ok.
  249. if (!IsTcpIpInstalled())
  250. {
  251. // We can't validate the full DNS name of the computer without TCP/IP
  252. // so the name passes.
  253. return true;
  254. }
  255. HRESULT hr =
  256. MyNetValidateName(state.GetFullComputerName(), NetSetupDnsMachine);
  257. if (FAILED(hr) and hr != Win32ToHresult(DNS_ERROR_NON_RFC_NAME))
  258. {
  259. // the full dns name is in use
  260. popup.Gripe(
  261. dialog,
  262. IDC_NEW_NAME,
  263. hr,
  264. String::format(
  265. IDS_VALIDATE_NAME_FAILED,
  266. state.GetFullComputerName().c_str()));
  267. return false;
  268. }
  269. return true;
  270. }
  271. // validates the short name, the full name, and the netbios names, raising
  272. // UI to complain on failures, returns false if any name fails, true if all
  273. // names pass.
  274. //
  275. // this is also good for the tcp/ip not installed case, as the edit control
  276. // limits the text length, and we decided not to allow '.' in netbios names
  277. // any longer
  278. bool
  279. ValidateComputerNames(HWND dialog)
  280. {
  281. LOG_FUNCTION(ValidateComputerNames);
  282. ASSERT(Win::IsWindow(dialog));
  283. State& state = State::GetInstance();
  284. if (!state.WasShortComputerNameChanged())
  285. {
  286. return true;
  287. }
  288. String shortName = state.GetShortComputerName();
  289. String message;
  290. switch (Dns::ValidateDnsLabelSyntax(shortName))
  291. {
  292. case Dns::VALID:
  293. {
  294. return ValidateNetbiosAndFullNames(dialog, shortName);
  295. }
  296. case Dns::TOO_LONG:
  297. {
  298. message =
  299. String::format(
  300. IDS_COMPUTER_NAME_TOO_LONG,
  301. shortName.c_str(),
  302. Dns::MAX_LABEL_LENGTH);
  303. break;
  304. }
  305. case Dns::NON_RFC:
  306. {
  307. message =
  308. String::format(
  309. IDS_NON_RFC_COMPUTER_NAME_SYNTAX,
  310. shortName.c_str());
  311. if (
  312. popup.MessageBox(
  313. dialog,
  314. message,
  315. MB_ICONWARNING | MB_YESNO) == IDYES)
  316. {
  317. return ValidateNetbiosAndFullNames(dialog, shortName);
  318. }
  319. HWND edit = Win::GetDlgItem(dialog, IDC_NEW_NAME);
  320. Win::SendMessage(edit, EM_SETSEL, 0, -1);
  321. Win::SetFocus(edit);
  322. return false;
  323. }
  324. case Dns::NUMERIC:
  325. {
  326. message =
  327. String::format(IDS_COMPUTER_NAME_NUMERIC, shortName.c_str());
  328. break;
  329. }
  330. case Dns::BAD_CHARS:
  331. {
  332. message =
  333. String::format(
  334. IDS_COMPUTER_NAME_HAS_BAD_CHARS,
  335. shortName.c_str());
  336. break;
  337. }
  338. case Dns::INVALID:
  339. {
  340. message =
  341. String::format(
  342. IDS_BAD_COMPUTER_NAME_SYNTAX,
  343. shortName.c_str());
  344. break;
  345. }
  346. default:
  347. {
  348. ASSERT(false);
  349. message =
  350. String::format(IDS_BAD_COMPUTER_NAME_SYNTAX, shortName.c_str());
  351. break;
  352. }
  353. }
  354. popup.Gripe(dialog, IDC_NEW_NAME, message);
  355. return false;
  356. }
  357. bool
  358. WorkgroupNameTooLong(const String& name)
  359. {
  360. LOG_FUNCTION2(WorkgroupNameTooLong, name);
  361. ASSERT(!name.empty());
  362. bool result = false;
  363. do
  364. {
  365. // first- cheap length test. Since a character will never be smaller
  366. // than 1 byte, if the number of characters exceeds the number of
  367. // bytes, we know it will never fit.
  368. if (name.length() > DNLEN)
  369. {
  370. result = true;
  371. break;
  372. }
  373. // second- check length of against corresponding UTF8 string
  374. // utf8bytes is the number of bytes (not characters) required to hold
  375. // the string in the UTF-8 character set.
  376. size_t oemBytes =
  377. static_cast<size_t>(
  378. // @@ why isn't this wrapped with a Win function?
  379. ::WideCharToMultiByte(
  380. CP_OEMCP,
  381. 0,
  382. name.c_str(),
  383. static_cast<int>(name.length()),
  384. 0,
  385. 0,
  386. 0,
  387. 0));
  388. LOG(String::format(L"name is %1!d! oem bytes", oemBytes));
  389. if (oemBytes > DNLEN)
  390. {
  391. LOG(L"oem length too long");
  392. result = true;
  393. break;
  394. }
  395. }
  396. while (0);
  397. LOG(String::format(L"name %1 too long", result ? L"is" : L"is NOT" ));
  398. return result;
  399. }
  400. bool
  401. ValidateDomainOrWorkgroupName(HWND dialog)
  402. {
  403. LOG_FUNCTION(ValidateDomainOrWorkgroupName);
  404. ASSERT(Win::IsWindow(dialog));
  405. if (!State::GetInstance().WasMembershipChanged())
  406. {
  407. return true;
  408. }
  409. HRESULT hr = S_OK;
  410. int nameId = 0;
  411. String name;
  412. if (Win::IsDlgButtonChecked(dialog, IDC_DOMAIN_BUTTON))
  413. {
  414. nameId = IDC_DOMAIN;
  415. name = Win::GetTrimmedDlgItemText(dialog, nameId);
  416. hr = MyNetValidateName(name, NetSetupDomain);
  417. if (hr == Win32ToHresult(DNS_ERROR_NON_RFC_NAME))
  418. {
  419. // accept non-rfc dns names. We have to check for the reachability
  420. // of the domain because NetValidateName will not bother to check
  421. // reachability for non-rfc names.
  422. hr = S_OK;
  423. if (!IsDomainReachable(name))
  424. {
  425. hr = Win32ToHresult(ERROR_NO_SUCH_DOMAIN);
  426. }
  427. }
  428. if (hr == Win32ToHresult(ERROR_NO_SUCH_DOMAIN))
  429. {
  430. // domain not found. Call the diagnostic error message dialog.
  431. ShowDcNotFoundErrorDialog(
  432. dialog,
  433. nameId,
  434. name,
  435. String::load(IDS_APP_TITLE),
  436. String::format(IDS_GENERIC_DC_NOT_FOUND_PARAM, name.c_str()),
  437. false,
  438. false);
  439. return false;
  440. }
  441. }
  442. else
  443. {
  444. nameId = IDC_WORKGROUP;
  445. name = Win::GetTrimmedDlgItemText(dialog, nameId);
  446. // we do our own length checking, as the NetValidateName API
  447. // does not return a distinct error code for length problems. 26968
  448. if (WorkgroupNameTooLong(name))
  449. {
  450. popup.Gripe(
  451. dialog,
  452. nameId,
  453. String::format(
  454. IDS_WORKGROUP_NAME_TOO_LONG,
  455. name.c_str(),
  456. DNLEN));
  457. return false;
  458. }
  459. hr = MyNetValidateName(name, NetSetupWorkgroup);
  460. }
  461. if (FAILED(hr))
  462. {
  463. popup.Gripe(
  464. dialog,
  465. nameId,
  466. hr,
  467. String::format(IDS_VALIDATE_NAME_FAILED, name.c_str()));
  468. return false;
  469. }
  470. return true;
  471. }
  472. bool
  473. IDChangesDialog::OnOkButton()
  474. {
  475. State& state = State::GetInstance();
  476. ASSERT(state.ChangesNeedSaving());
  477. Win::CursorSetting cursor(IDC_WAIT);
  478. String preconditionErrorMessage = CheckPreconditions();
  479. if (!preconditionErrorMessage.empty())
  480. {
  481. popup.Error(
  482. hwnd,
  483. preconditionErrorMessage);
  484. return false;
  485. }
  486. // computer primary DNS name has already been validated by
  487. // MoreChangesDialog
  488. state.SetShortComputerName(Win::GetTrimmedDlgItemText(hwnd, IDC_NEW_NAME));
  489. bool workgroup =
  490. Win::IsDlgButtonChecked(hwnd, IDC_WORKGROUP_BUTTON);
  491. state.SetIsMemberOfWorkgroup(workgroup);
  492. if (workgroup)
  493. {
  494. state.SetDomainName(
  495. Win::GetTrimmedDlgItemText(hwnd, IDC_WORKGROUP));
  496. }
  497. else
  498. {
  499. state.SetDomainName(
  500. Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN));
  501. }
  502. // 341483
  503. if (state.GetShortComputerName().icompare(state.GetDomainName()) == 0)
  504. {
  505. // can't have domain/workgroup name same as computer name
  506. popup.Gripe(
  507. hwnd,
  508. IDC_NEW_NAME,
  509. workgroup
  510. ? IDS_COMPUTER_NAME_EQUALS_WORKGROUP_NAME
  511. : IDS_COMPUTER_NAME_EQUALS_DOMAIN_NAME);
  512. return false;
  513. }
  514. if (
  515. !ValidateComputerNames(hwnd)
  516. || !ValidateDomainOrWorkgroupName(hwnd))
  517. {
  518. return false;
  519. }
  520. if (state.SaveChanges(hwnd))
  521. {
  522. popup.Info(hwnd, IDS_MUST_REBOOT);
  523. State::GetInstance().SetChangesMadeThisSession(true);
  524. return true;
  525. }
  526. return false;
  527. }
  528. bool
  529. IDChangesDialog::OnCommand(
  530. HWND /* windowFrom */ ,
  531. unsigned controlIDFrom,
  532. unsigned code)
  533. {
  534. State& state = State::GetInstance();
  535. switch (controlIDFrom)
  536. {
  537. case IDC_MORE:
  538. {
  539. if (code == BN_CLICKED)
  540. {
  541. MoreChangesDialog dlg(fIsPersonal);
  542. if (dlg.ModalExecute(hwnd) == MoreChangesDialog::CHANGES_MADE)
  543. {
  544. Win::SetDlgItemText(
  545. hwnd,
  546. IDC_FULL_NAME,
  547. state.GetFullComputerName());
  548. enable(hwnd);
  549. }
  550. }
  551. break;
  552. }
  553. case IDOK:
  554. {
  555. if (code == BN_CLICKED)
  556. {
  557. if (OnOkButton())
  558. {
  559. HRESULT unused = Win::EndDialog(hwnd, controlIDFrom);
  560. ASSERT(SUCCEEDED(unused));
  561. }
  562. }
  563. break;
  564. }
  565. case IDCANCEL:
  566. {
  567. if (code == BN_CLICKED)
  568. {
  569. HRESULT unused = Win::EndDialog(hwnd, controlIDFrom);
  570. ASSERT(SUCCEEDED(unused));
  571. }
  572. break;
  573. }
  574. case IDC_WORKGROUP_BUTTON:
  575. case IDC_DOMAIN_BUTTON:
  576. {
  577. if (code == BN_CLICKED)
  578. {
  579. bool workgroup =
  580. Win::IsDlgButtonChecked(hwnd, IDC_WORKGROUP_BUTTON);
  581. state.SetIsMemberOfWorkgroup(workgroup);
  582. if (workgroup)
  583. {
  584. state.SetDomainName(
  585. Win::GetTrimmedDlgItemText(hwnd, IDC_WORKGROUP));
  586. }
  587. else
  588. {
  589. state.SetDomainName(
  590. Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN));
  591. }
  592. enable(hwnd);
  593. }
  594. break;
  595. }
  596. case IDC_WORKGROUP:
  597. case IDC_DOMAIN:
  598. {
  599. if (code == EN_CHANGE)
  600. {
  601. SetChanged(controlIDFrom);
  602. state.SetDomainName(
  603. Win::GetTrimmedDlgItemText(hwnd, controlIDFrom));
  604. enable(hwnd);
  605. }
  606. break;
  607. }
  608. case IDC_NEW_NAME:
  609. {
  610. if (code == EN_CHANGE)
  611. {
  612. // the first time this path is hit, it is because of the SetText
  613. // in OnInit. If that is the case, then don't overwrite the
  614. // short computer name, or we'll clobber the existing netbios name
  615. // wait till the second time thru this path (which will be due
  616. // do a user keypress)
  617. if (!isInitializing)
  618. {
  619. state.SetShortComputerName(
  620. Win::GetTrimmedDlgItemText(hwnd, controlIDFrom));
  621. Win::SetDlgItemText(
  622. hwnd,
  623. IDC_FULL_NAME,
  624. state.GetFullComputerName());
  625. }
  626. SetChanged(controlIDFrom);
  627. enable(hwnd);
  628. }
  629. break;
  630. }
  631. default:
  632. {
  633. break;
  634. }
  635. }
  636. return true;
  637. }