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.

2484 lines
73 KiB

  1. #include "pch.h"
  2. #pragma hdrstop
  3. #include <ntsam.h>
  4. #include <lmerr.h>
  5. #include <wincred.h>
  6. #include "afilexp.h"
  7. #include "dsgetdc.h"
  8. #include "ncatlui.h"
  9. #include "nccom.h"
  10. #include "nceh.h"
  11. #include "ncerror.h"
  12. #include "ncident.h"
  13. #include "ncmisc.h"
  14. #include "ncreg.h"
  15. #include "ncsetup.h"
  16. #include "ncsvc.h"
  17. #include "ncui.h"
  18. #include "resource.h"
  19. #include "wizard.h"
  20. #include "nslog.h"
  21. #include "windns.h"
  22. // header filename clash between config\inc\netsetup.h and
  23. // private\net\inc\netsetup.h where this prototype lives.
  24. // fix later.
  25. //
  26. EXTERN_C
  27. NET_API_STATUS
  28. NET_API_FUNCTION
  29. NetpUpgradePreNT5JoinInfo( VOID );
  30. // Setup Wizard Global - Only used during setup.
  31. extern CWizard * g_pSetupWizard;
  32. //
  33. // NOTE: Set breakpoints in JoinDomainWorkThrd if debugging join problems
  34. //
  35. static const UINT PWM_JOINFAILURE = WM_USER+1201;
  36. static const UINT PWM_JOINSUCCESS = WM_USER+1202;
  37. static const INT MAX_USERNAME_LENGTH = UNLEN;
  38. // the number of bytes in a full DNS name to reserve for stuff
  39. // netlogon pre-/appends to DNS names when registering them
  40. static const INT SRV_RECORD_RESERVE = 100;
  41. static const INT MAX_DOMAINNAME_LENGTH = DNS_MAX_NAME_LENGTH - SRV_RECORD_RESERVE;
  42. static const INT MAX_WORKGROUPNAME_LENGTH = 15;
  43. static const INT MAX_TITLEBASE = 128;
  44. static const INT MAX_TITLENEW = 256;
  45. static const INT MAX_NAME_LENGTH = max( max( max( SAM_MAX_PASSWORD_LENGTH,
  46. MAX_USERNAME_LENGTH ),
  47. MAX_COMPUTERNAME_LENGTH ),
  48. MAX_DOMAINNAME_LENGTH) + 1;
  49. const int nrgIdc[] = {EDT_WORKGROUPJOIN_NAME, EDT_DOMAINJOIN_NAME, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, TXT_JOIN_WORKGROUP_LINE2};
  50. const int nrgIdcWorkgroup[] = {EDT_WORKGROUPJOIN_NAME};
  51. const int nrgIdcDomain[] = {EDT_DOMAINJOIN_NAME};
  52. const c_dwDomainJoinWaitDelay = 10000;
  53. static const WCHAR c_szNetMsg[] = L"netmsg.dll";
  54. static const WCHAR c_szIpParameters[] = L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters";
  55. static const WCHAR c_szSyncDomainWithMembership[] = L"SyncDomainWithMembership";
  56. static const WCHAR c_szWinlogonPath[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon";
  57. static const WCHAR c_szRunNetAccessWizard[] = L"RunNetAccessWizard";
  58. static const WCHAR c_szAfSectionGuiUnattended[] = L"GuiUnattended";
  59. static const WCHAR c_szAfAutoLogonAccountCreation[] = L"AutoLogonAccountCreation";
  60. extern const WCHAR c_szAfSectionIdentification[]; // L"Identification";
  61. extern const WCHAR c_szAfComputerName[]; // L"ComputerName";
  62. extern const WCHAR c_szAfJoinWorkgroup[]; // L"JoinWorkgroup";
  63. extern const WCHAR c_szAfJoinDomain[]; // L"JoinDomain";
  64. extern const WCHAR c_szAfDomainAdmin[]; // L"DomainAdmin";
  65. extern const WCHAR c_szAfDomainAdminPassword[]; // L"DomainAdminPassword";
  66. extern const WCHAR c_szAfSectionNetworking[]; // L"Networking";
  67. extern const WCHAR c_szAfUpgradeFromProduct[]; // L"UpgradeFromProduct";
  68. extern const WCHAR c_szAfWin95[]; // L"Windows95";
  69. extern const WCHAR c_szSvcWorkstation[]; // L"LanmanWorkstation";
  70. extern const WCHAR c_szAfMachineObjectOU[]; // L"MachineObjectOU";
  71. extern const WCHAR c_szAfUnsecureJoin[]; // L"DoOldStyleDomainJoin";
  72. extern const WCHAR c_szAfBuildNumber[]; // L"BuildNumber";
  73. // For Secure Domain Join Support, the computer account password
  74. extern const WCHAR c_szAfComputerPassword[]; // L"ComputerPassword";
  75. typedef struct _tagJoinData
  76. {
  77. BOOL fUpgraded;
  78. BOOL fUnattendedFailed;
  79. CNetCfgIdentification * pIdent;
  80. HCURSOR hClassCursor;
  81. HCURSOR hOldCursor;
  82. // Used by join thread
  83. //
  84. HWND hwndDlg;
  85. // Set from answer file or user input then supplied to the join thread
  86. // as the join parameters
  87. //
  88. DWORD dwJoinFlag;
  89. WCHAR szUserName[MAX_USERNAME_LENGTH + 1];
  90. WCHAR szPassword[SAM_MAX_PASSWORD_LENGTH + 1];
  91. WCHAR szDomain[MAX_DOMAINNAME_LENGTH + 1];
  92. WCHAR szComputerPassword[SAM_MAX_PASSWORD_LENGTH + 1];
  93. WCHAR * pszMachineObjectOU;
  94. } JoinData;
  95. typedef enum
  96. {
  97. PSW_JOINEDDOMAIN = 2,
  98. PSW_JOINFAILED = 3
  99. } POSTSETUP_STATE;
  100. //
  101. // Function: IsRunningOnPersonal
  102. //
  103. // Purpose: Determines whether running on Whistler Personal
  104. //
  105. // Returns: Returns true if running on Personal - FALSE otherwise
  106. BOOL
  107. IsRunningOnPersonal(
  108. VOID
  109. )
  110. {
  111. TraceFileFunc(ttidGuiModeSetup);
  112. OSVERSIONINFOEXW OsVer = {0};
  113. ULONGLONG ConditionMask = 0;
  114. OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  115. OsVer.wSuiteMask = VER_SUITE_PERSONAL;
  116. OsVer.wProductType = VER_NT_WORKSTATION;
  117. VER_SET_CONDITION(ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
  118. VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_AND);
  119. return VerifyVersionInfo(&OsVer,
  120. VER_PRODUCT_TYPE | VER_SUITENAME,
  121. ConditionMask
  122. );
  123. }
  124. //
  125. // Function: IsValidDomainName
  126. //
  127. // Purpose: Determines whether the domain name is valid.
  128. //
  129. // Returns: See win32 documentation on DnsValidateName.
  130. //
  131. // Author: Alok Sinha
  132. //
  133. DNS_STATUS IsValidDomainName (HWND hwndDlg)
  134. {
  135. TraceFileFunc(ttidGuiModeSetup);
  136. WCHAR szDomain[MAX_DOMAINNAME_LENGTH+1];
  137. HWND hwndEdit;
  138. hwndEdit = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME);
  139. Assert(0 != GetWindowTextLength(hwndEdit));
  140. GetWindowText(hwndEdit, szDomain, MAX_DOMAINNAME_LENGTH + 1);
  141. return DnsValidateName( szDomain,
  142. DnsNameDomain );
  143. }
  144. //
  145. // Function: SetCursorToHourglass
  146. //
  147. // Purpose: Changes the cursor to hourglass.
  148. //
  149. // Returns: Nothing
  150. //
  151. // Author: asinha 3/28/2001
  152. //
  153. VOID SetCursorToHourglass (HWND hwndDlg, JoinData *pData)
  154. {
  155. TraceFileFunc(ttidGuiModeSetup);
  156. Assert( pData != NULL);
  157. if ( pData )
  158. {
  159. pData->hClassCursor = (HCURSOR)GetClassLongPtr( hwndDlg, GCLP_HCURSOR );
  160. SetClassLongPtr( hwndDlg, GCLP_HCURSOR, (LONG_PTR)NULL );
  161. pData->hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );
  162. SetCapture( hwndDlg );
  163. }
  164. return;
  165. }
  166. //
  167. // Function: RestoreCursor
  168. //
  169. // Purpose: Changes the cursor back to its orginal state.
  170. //
  171. // Returns: Nothing
  172. //
  173. // Author: asinha 3/28/2001
  174. //
  175. VOID RestoreCursor (HWND hwndDlg, JoinData *pData)
  176. {
  177. TraceFileFunc(ttidGuiModeSetup);
  178. Assert( pData != NULL);
  179. if ( pData )
  180. {
  181. if ( pData->hClassCursor )
  182. {
  183. SetClassLongPtr( hwndDlg, GCLP_HCURSOR, (LONG_PTR)pData->hClassCursor );
  184. pData->hClassCursor = NULL;
  185. }
  186. if ( pData->hOldCursor )
  187. {
  188. SetCursor( pData->hOldCursor );
  189. pData->hOldCursor = NULL;
  190. }
  191. ReleaseCapture();
  192. }
  193. return;
  194. }
  195. //
  196. // Function: NotifyPostSetupWizard
  197. //
  198. // Purpose: Subclass the edit control so we can enable/disable the
  199. // Next button as the content of the name edit control changes
  200. //
  201. // Parameters: State - Final Join State
  202. //
  203. // Returns: nothing
  204. //
  205. VOID NotifyPostSetupWizard(POSTSETUP_STATE State, CWizard * pWizard)
  206. {
  207. TraceFileFunc(ttidGuiModeSetup);
  208. HKEY hkey;
  209. HRESULT hr = S_OK;
  210. BOOL fRunNaWizard = TRUE;
  211. CSetupInfFile csif;
  212. // If this is an upgrade or an unattended install, or it's a server do nothing
  213. //
  214. if (IsUpgrade(pWizard) || IsUnattended(pWizard) || (PRODUCT_WORKSTATION != ProductType(pWizard)) )
  215. {
  216. fRunNaWizard = FALSE;
  217. }
  218. // Is there an unattended flag to override default behavior?
  219. if (IsUnattended(pWizard))
  220. {
  221. hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile,
  222. NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
  223. if (SUCCEEDED(hr))
  224. {
  225. BOOL fValue = FALSE;
  226. hr = csif.HrGetStringAsBool(c_szAfSectionGuiUnattended,
  227. c_szAfAutoLogonAccountCreation,
  228. &fValue);
  229. if (SUCCEEDED(hr) && fValue)
  230. {
  231. fRunNaWizard = fValue;
  232. }
  233. }
  234. hr = S_OK;
  235. }
  236. if (fRunNaWizard)
  237. {
  238. hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szWinlogonPath,
  239. KEY_WRITE, &hkey);
  240. if (SUCCEEDED(hr))
  241. {
  242. hr = HrRegSetDword (hkey, c_szRunNetAccessWizard, (DWORD)State);
  243. TraceTag(ttidWizard, "NotifyPostSetupWizard - State = %d",(DWORD)State);
  244. RegCloseKey(hkey);
  245. }
  246. }
  247. TraceError("WJOIN.CPP - NotifyPostSetupWizard",hr);
  248. }
  249. //
  250. // Function: JoinEditSubclassProc
  251. //
  252. // Purpose: Subclass the edit control so we can enable/disable the
  253. // Next button as the content of the name edit control changes
  254. //
  255. // Parameters: std for a window proc
  256. //
  257. // Returns: std for a window proc
  258. //
  259. STDAPI JoinEditSubclassProc(HWND hwnd, UINT wMsg,
  260. WPARAM wParam, LPARAM lParam)
  261. {
  262. TraceFileFunc(ttidGuiModeSetup);
  263. LONG lReturn;
  264. HWND hwndDlg = GetParent(hwnd);
  265. lReturn = CallWindowProc((WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA),
  266. hwnd, wMsg, wParam, lParam);
  267. // If we processing a character send the message through the regular proc
  268. if (WM_CHAR == wMsg)
  269. {
  270. CWizard * pWizard =
  271. reinterpret_cast<CWizard *> (::GetWindowLongPtr(hwndDlg, DWLP_USER));
  272. Assert(NULL != pWizard);
  273. if ( !pWizard )
  274. {
  275. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
  276. return lReturn;
  277. }
  278. JoinData * pData = reinterpret_cast<JoinData *>
  279. (pWizard->GetPageData(IDD_Join));
  280. Assert(NULL != pData);
  281. if ( !pData ) {
  282. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
  283. return lReturn;
  284. }
  285. if (!IsUnattended(pWizard) ||
  286. (IsUnattended(pWizard) && pData->fUnattendedFailed))
  287. {
  288. if (0 == GetWindowTextLength(hwnd))
  289. {
  290. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
  291. }
  292. else
  293. {
  294. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
  295. }
  296. }
  297. }
  298. return lReturn;
  299. }
  300. HRESULT HrGetIdentInterface(CNetCfgIdentification ** ppIdent)
  301. {
  302. TraceFileFunc(ttidGuiModeSetup);
  303. HRESULT hr = S_OK;
  304. *ppIdent = new CNetCfgIdentification;
  305. if (NULL == *ppIdent)
  306. hr = E_OUTOFMEMORY;
  307. TraceHr(ttidWizard, FAL, hr, FALSE, "HrGetIdentInterface");
  308. return hr;
  309. }
  310. //
  311. // Function: IdsFromIdentError
  312. //
  313. // Purpose: Map a error code into a display string.
  314. //
  315. // Parameters: hr [IN] - Error code to map
  316. //
  317. // Returns: INT, the string ID of the corresponding error message
  318. //
  319. INT IdsFromIdentError(HRESULT hr, BOOL fWorkgroup)
  320. {
  321. TraceFileFunc(ttidGuiModeSetup);
  322. INT ids = -1;
  323. switch (hr)
  324. {
  325. case HRESULT_FROM_WIN32(ERROR_NO_TRUST_SAM_ACCOUNT):
  326. ids = IDS_DOMMGR_CANT_CONNECT_DC_PW;
  327. break;
  328. case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH):
  329. case HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN):
  330. ids = IDS_DOMMGR_CANT_FIND_DC1;
  331. break;
  332. case HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD):
  333. ids = IDS_DOMMGR_INVALID_PASSWORD;
  334. break;
  335. case HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE):
  336. ids = IDS_DOMMGR_NETWORK_UNREACHABLE;
  337. break;
  338. case HRESULT_FROM_WIN32(ERROR_ACCOUNT_DISABLED):
  339. case HRESULT_FROM_WIN32(ERROR_LOGON_FAILURE):
  340. case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED):
  341. ids = IDS_DOMMGR_ACCESS_DENIED;
  342. break;
  343. case HRESULT_FROM_WIN32(ERROR_SESSION_CREDENTIAL_CONFLICT):
  344. ids = IDS_DOMMGR_CREDENTIAL_CONFLICT;
  345. break;
  346. case NETCFG_E_ALREADY_JOINED:
  347. ids = IDS_DOMMGR_ALREADY_JOINED;
  348. break;
  349. case NETCFG_E_NAME_IN_USE:
  350. ids = IDS_DOMMGR_NAME_IN_USE;
  351. break;
  352. case NETCFG_E_NOT_JOINED:
  353. ids = IDS_DOMMGR_NOT_JOINED;
  354. break;
  355. case NETCFG_E_INVALID_DOMAIN:
  356. case HRESULT_FROM_WIN32(ERROR_INVALID_NAME):
  357. if (fWorkgroup)
  358. ids = IDS_DOMMGR_INVALID_WORKGROUP;
  359. else
  360. ids = IDS_DOMMGR_INVALID_DOMAIN;
  361. break;
  362. default:
  363. ids = IDS_E_UNEXPECTED;
  364. break;
  365. }
  366. return ids;
  367. }
  368. //
  369. // Function: SzFromError
  370. //
  371. // Purpose: Convert an error code into a message displayable to the user.
  372. // The error message could come from our resources, or from netmsg.dll
  373. //
  374. // Parameters: hr [IN] - The error to map
  375. // fWorkgroup [IN] - Flag to provide "workgroup" flavored error
  376. // messages for some cases.
  377. //
  378. // Returns: PCWSTR, Pointer to a static buffer containing the error message
  379. //
  380. PCWSTR SzFromError(HRESULT hr, BOOL fWorkgroup)
  381. {
  382. TraceFileFunc(ttidGuiModeSetup);
  383. static WCHAR szErrorMsg[1024];
  384. INT nIds = IdsFromIdentError(hr, fWorkgroup);
  385. // If the error string returned is unexpected, it means we couldn't
  386. // a local match for the string. Search netmsg.dll if the errors'
  387. // range is correct
  388. if (IDS_E_UNEXPECTED == nIds)
  389. {
  390. // Extract the error code from the HRESULT
  391. DWORD dwErr = ((DWORD)hr & 0x0000FFFF);
  392. if ((NERR_BASE <= dwErr) && (MAX_NERR >= dwErr))
  393. {
  394. // The error is within the range hosted by netmsg.dll
  395. HMODULE hModule = LoadLibraryEx(c_szNetMsg, NULL,
  396. LOAD_LIBRARY_AS_DATAFILE);
  397. if (NULL != hModule)
  398. {
  399. // Try to locate the error string
  400. DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
  401. FORMAT_MESSAGE_IGNORE_INSERTS,
  402. (LPVOID)hModule,
  403. dwErr,
  404. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  405. szErrorMsg, // Output buffer
  406. 1024, // szErrorMsg in characters.
  407. NULL);
  408. FreeLibrary(hModule);
  409. if (dwRet)
  410. {
  411. // We successfully found an error message
  412. // Remove the trailing newline that format message adds
  413. // This string is concatenated with another and the newline
  414. // messes up the appearance.
  415. //
  416. // Raid 146173 - scottbri
  417. //
  418. int nLen = wcslen(szErrorMsg);
  419. if ((nLen>2) && (L'\r' == szErrorMsg[nLen-2]))
  420. {
  421. szErrorMsg[nLen-2] = 0;
  422. }
  423. return szErrorMsg;
  424. }
  425. }
  426. }
  427. else if ( (dwErr >= 9000) && (dwErr <= 9999) )
  428. {
  429. // The error code from 9000-9999 is dns error code in which
  430. // case show the dns error message.
  431. //
  432. DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  433. FORMAT_MESSAGE_IGNORE_INSERTS,
  434. (LPVOID)NULL,
  435. dwErr,
  436. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  437. szErrorMsg, // Output buffer
  438. 1024, // szErrorMsg in characters.
  439. NULL);
  440. if (dwRet)
  441. {
  442. // We successfully found an error message
  443. // Remove the trailing newline that format message adds
  444. int nLen = wcslen(szErrorMsg);
  445. if ((nLen>2) && (L'\r' == szErrorMsg[nLen-2]))
  446. {
  447. szErrorMsg[nLen-2] = 0;
  448. }
  449. return szErrorMsg;
  450. }
  451. }
  452. }
  453. // Load the error found
  454. wcscpy(szErrorMsg, SzLoadIds(nIds));
  455. return szErrorMsg;
  456. }
  457. //
  458. // Function: GetJoinNameIIDFromSelection
  459. //
  460. // Purpose: Get the edit box for the workgroup / domain dialog depending on the current user selection
  461. //
  462. // Parameters: hwndDlg - The join domain dialog
  463. //
  464. // Returns:
  465. //
  466. inline DWORD GetJoinNameIIDFromSelection(HWND hwndDlg)
  467. {
  468. if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_WORKGROUP))
  469. {
  470. return EDT_WORKGROUPJOIN_NAME;
  471. }
  472. else
  473. {
  474. return EDT_DOMAINJOIN_NAME;
  475. }
  476. }
  477. //
  478. // Function: UpdateNextBackBttns
  479. //
  480. // Purpose:
  481. //
  482. // Parameters:
  483. //
  484. // Returns:
  485. //
  486. VOID UpdateNextBackBttns(HWND hwndDlg)
  487. {
  488. TraceFileFunc(ttidGuiModeSetup);
  489. int b = PSWIZB_BACK;
  490. if (0 != GetWindowTextLength(GetDlgItem(hwndDlg, GetJoinNameIIDFromSelection(hwndDlg))))
  491. {
  492. b |= PSWIZB_NEXT;
  493. }
  494. PropSheet_SetWizButtons(GetParent(hwndDlg), b);
  495. }
  496. //
  497. // Function: EnableAndDisableWorkGroupDomainControls
  498. //
  499. // Purpose: Disable the domain/workgroup edit boxes depending on the current user selection
  500. //
  501. // Parameters: hwndDlg - The join domain dialog
  502. //
  503. // Returns:
  504. //
  505. VOID EnableAndDisableWorkGroupDomainControls(HWND hwndDlg)
  506. {
  507. if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_WORKGROUP))
  508. {
  509. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcWorkgroup), nrgIdcWorkgroup, TRUE);
  510. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcDomain), nrgIdcDomain, FALSE);
  511. }
  512. else
  513. {
  514. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcDomain), nrgIdcDomain, TRUE);
  515. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcWorkgroup), nrgIdcWorkgroup, FALSE);
  516. }
  517. }
  518. //
  519. // Function: JoinUpdatePromptText
  520. //
  521. // Purpose: Update the prompt text
  522. //
  523. // Parameters: hwndDlg [IN] - Current dialog handle
  524. //
  525. // Returns: Nothing
  526. //
  527. VOID JoinUpdatePromptText(HWND hwndDlg)
  528. {
  529. TraceFileFunc(ttidGuiModeSetup);
  530. HWND hwndDomain = NULL;
  531. int idsNew = IDS_WORKGROUP;
  532. int idsOld = IDS_DOMAIN;
  533. WCHAR szDomain[MAX_DOMAINNAME_LENGTH + 1];
  534. JoinData * pData=NULL;
  535. CWizard * pWizard = NULL;
  536. // Based on the button selected, update the Prompt text & dialog box
  537. if (!IsDlgButtonChecked(hwndDlg, BTN_JOIN_WORKGROUP))
  538. {
  539. hwndDomain = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME);
  540. idsNew = IDS_DOMAIN;
  541. idsOld = IDS_WORKGROUP;
  542. }
  543. else
  544. {
  545. hwndDomain = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME);
  546. }
  547. Assert(NULL != hwndDomain);
  548. EnableAndDisableWorkGroupDomainControls(hwndDlg);
  549. // Update the domain/workgroup only if the current contents
  550. // are the default workgroup/domain
  551. GetWindowText(hwndDomain, szDomain, celems(szDomain));
  552. if (0 == lstrcmpW(szDomain, SzLoadIds(idsOld)))
  553. {
  554. SetWindowText(hwndDomain, SzLoadIds(idsNew));
  555. }
  556. // Update the back/next buttons based on the selected button. See bug 355978
  557. pWizard = (CWizard *) ::GetWindowLongPtr(hwndDlg, DWLP_USER);
  558. Assert(NULL != pWizard);
  559. if ( pWizard)
  560. {
  561. pData = (JoinData *) pWizard->GetPageData(IDD_Join);
  562. Assert(NULL != pData);
  563. }
  564. if ( pWizard && pData )
  565. {
  566. if (!IsUnattended(pWizard) ||
  567. (IsUnattended(pWizard) && pData->fUnattendedFailed))
  568. {
  569. if (0 == GetWindowTextLengthW(hwndDomain))
  570. {
  571. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
  572. }
  573. else
  574. {
  575. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
  576. }
  577. }
  578. }
  579. }
  580. //
  581. // Function: UpdateJoinUsingComputerRole
  582. //
  583. // Purpose:
  584. //
  585. // Parameters:
  586. //
  587. // Returns:
  588. //
  589. VOID UpdateJoinUsingComputerRole(HWND hwndDlg,
  590. CWizard * pWizard)
  591. {
  592. TraceFileFunc(ttidGuiModeSetup);
  593. HRESULT hr;
  594. INetCfg * pNetCfg = pWizard->PNetCfg();
  595. JoinData * pData = reinterpret_cast<JoinData *>
  596. (pWizard->GetPageData(IDD_Join));
  597. Assert(NULL != pNetCfg);
  598. Assert(NULL != pData);
  599. Assert(NULL != pData->pIdent);
  600. PWSTR pszwText = NULL;
  601. DWORD computer_role;
  602. int nIdc;
  603. Assert(NULL != pData->pIdent);
  604. if(!pData || !pData->pIdent)
  605. {
  606. return;
  607. }
  608. hr = pData->pIdent->GetComputerRole(&computer_role);
  609. if (SUCCEEDED(hr))
  610. {
  611. if (computer_role & GCR_STANDALONE)
  612. {
  613. // Get the workgroup name
  614. hr = pData->pIdent->GetWorkgroupName(&pszwText);
  615. Assert(NULL != pszwText);
  616. Assert(lstrlenW(pszwText) <= MAX_WORKGROUPNAME_LENGTH);
  617. nIdc = BTN_JOIN_WORKGROUP;
  618. }
  619. else
  620. {
  621. // Get the domain name
  622. hr = pData->pIdent->GetDomainName(&pszwText);
  623. Assert(NULL != pszwText);
  624. Assert(lstrlenW(pszwText) <= MAX_DOMAINNAME_LENGTH);
  625. nIdc = BTN_JOIN_DOMAIN;
  626. }
  627. }
  628. if (SUCCEEDED(hr))
  629. {
  630. HWND hwndEdit = GetDlgItem(hwndDlg, nIdc == BTN_JOIN_DOMAIN ? EDT_DOMAINJOIN_NAME : EDT_WORKGROUPJOIN_NAME);
  631. Assert(NULL != hwndEdit);
  632. SetWindowText(hwndEdit, pszwText);
  633. CoTaskMemFree(pszwText);
  634. CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP,
  635. BTN_JOIN_DOMAIN, nIdc);
  636. }
  637. else
  638. {
  639. HWND hwndEdit = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME);
  640. Assert(NULL != hwndEdit);
  641. SetWindowText(hwndEdit, SzLoadIds(IDS_WORKGROUP));
  642. CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP,
  643. BTN_JOIN_DOMAIN, BTN_JOIN_WORKGROUP);
  644. TraceHr(ttidWizard, FAL, hr, FALSE,
  645. "UpdateJoinUsingComputerRole - Unable to determine role, using default");
  646. }
  647. JoinUpdatePromptText(hwndDlg);
  648. }
  649. //
  650. // Function: JoinDefaultWorkgroup
  651. //
  652. // Purpose: Join the machine to the workgroup "WORKGROUP"
  653. //
  654. // Parameters: pWizard [IN] - Ptr to a wizard instance, containing
  655. // hopefully a INetCfg instance pointer
  656. // hwndDlg [IN] - HWND to parent error dialogs against
  657. //
  658. // Returns: nothing
  659. //
  660. void JoinDefaultWorkgroup(CWizard *pWizard, HWND hwndDlg)
  661. {
  662. TraceFileFunc(ttidGuiModeSetup);
  663. // Join default workgroup.
  664. CNetCfgIdentification *pINetid = NULL;
  665. HRESULT hr = S_OK;
  666. hr = HrGetIdentInterface(&pINetid);
  667. if (S_OK == hr)
  668. {
  669. if (IsRunningOnPersonal())
  670. {
  671. hr = pINetid->JoinWorkgroup(SzLoadIds(IDS_WORKGROUP_PERSONAL));
  672. }
  673. else
  674. {
  675. hr = pINetid->JoinWorkgroup(SzLoadIds(IDS_WORKGROUP));
  676. }
  677. if (SUCCEEDED(hr))
  678. {
  679. hr = pINetid->Validate();
  680. if (SUCCEEDED(hr))
  681. {
  682. hr = pINetid->Apply();
  683. }
  684. }
  685. if (FAILED(hr))
  686. {
  687. if (UM_FULLUNATTENDED == pWizard->GetUnattendedMode())
  688. {
  689. // Raid 380374: no UI allowed if in full unattended mode ..
  690. NetSetupLogStatusV( LogSevError,
  691. SzLoadIds (IDS_E_UNATTENDED_JOIN_DEFAULT_WROKGROUP));
  692. }
  693. else
  694. {
  695. MessageBox(GetParent(hwndDlg), SzFromError(hr, TRUE),
  696. SzLoadIds(IDS_SETUP_CAPTION), MB_OK);
  697. }
  698. goto Done;
  699. }
  700. }
  701. else
  702. {
  703. AssertSz(0,"Cannot find the INegCfgIdentification interface!");
  704. }
  705. Done:
  706. delete pINetid;
  707. TraceHr(ttidWizard, FAL, hr, FALSE, "JoinDefaultWorkgroup");
  708. }
  709. BOOL OnJoinSuccess(HWND hwndDlg)
  710. {
  711. TraceFileFunc(ttidGuiModeSetup);
  712. CWizard * pWizard =
  713. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  714. Assert(NULL != pWizard);
  715. if ( !pWizard ) {
  716. return TRUE;
  717. }
  718. // Reset the Wait Cursor
  719. JoinData * pData = reinterpret_cast<JoinData *>
  720. (pWizard->GetPageData(IDD_Join));
  721. Assert(NULL != pData);
  722. if ( !pData ) {
  723. return TRUE;
  724. }
  725. RestoreCursor( hwndDlg, pData );
  726. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, TRUE);
  727. EnableAndDisableWorkGroupDomainControls(hwndDlg);
  728. if (!(IsUnattended(pWizard) && pData->fUpgraded))
  729. {
  730. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
  731. }
  732. // Goto the Exit page
  733. pWizard->SetPageDirection(IDD_Join, NWPD_BACKWARD);
  734. HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_Exit);
  735. PostMessage(GetParent(hwndDlg), PSM_SETCURSEL, 0,
  736. (LPARAM)(HPROPSHEETPAGE)hPage);
  737. return TRUE;
  738. }
  739. BOOL OnJoinFailure(HWND hwndDlg, LPARAM lParam)
  740. {
  741. TraceFileFunc(ttidGuiModeSetup);
  742. JoinData * pData = NULL;
  743. BOOL fWorkgroup;
  744. tstring str;
  745. HRESULT hr = (HRESULT)lParam;
  746. CWizard * pWizard =
  747. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  748. Assert(NULL != pWizard);
  749. if (pWizard)
  750. {
  751. pData = reinterpret_cast<JoinData *>(pWizard->GetPageData(IDD_Join));
  752. Assert(NULL != pData);
  753. }
  754. fWorkgroup = !IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN);
  755. if (fWorkgroup)
  756. {
  757. str = SzLoadIds(IDS_JOIN_E_WORKGROUP_MSG);
  758. }
  759. else
  760. {
  761. if (pData && (pData->dwJoinFlag & JDF_WIN9x_UPGRADE))
  762. {
  763. str = SzLoadIds(IDS_JOIN_E_DOMAIN_WIN9X_MSG_1);
  764. str += SzLoadIds(IDS_JOIN_E_DOMAIN_WIN9X_MSG_2);
  765. }
  766. else
  767. {
  768. // Raid 372087: removed additional text
  769. str = SzLoadIds(IDS_JOIN_E_DOMAIN_MSG);
  770. }
  771. }
  772. // If an error actually occurred, ask the user if they want to ignore
  773. // the error and proceed. Note Unattended can succeed but require us
  774. // to stay on the page so this function is envoked to do this.
  775. if (FAILED(hr))
  776. {
  777. if (IDYES == NcMsgBox(GetParent(hwndDlg),
  778. IDS_SETUP_CAPTION, IDS_JOIN_FAILURE,
  779. MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2,
  780. SzFromError(hr, fWorkgroup), str.c_str()))
  781. {
  782. // User choose to proceed in spite of the failure, go to the Exit page
  783. if (!fWorkgroup)
  784. {
  785. NotifyPostSetupWizard(PSW_JOINFAILED, pWizard);
  786. }
  787. OnJoinSuccess(hwndDlg);
  788. return TRUE;
  789. }
  790. }
  791. if (pData)
  792. {
  793. // Reset the Wait Cursor
  794. RestoreCursor( hwndDlg, pData );
  795. }
  796. // Make sure the page is visible.
  797. if (g_pSetupWizard != NULL)
  798. {
  799. g_pSetupWizard->PSetupData()->ShowHideWizardPage(TRUE);
  800. }
  801. // The user wants to stay and correct whatever's wrong
  802. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, TRUE);
  803. EnableAndDisableWorkGroupDomainControls(hwndDlg);
  804. UpdateNextBackBttns(hwndDlg);
  805. return TRUE;
  806. }
  807. // ONLY return failure if the workstation is installed and it doesn't start for 2 minutes
  808. HRESULT HrWorkstationStart(HWND hwndDlg)
  809. {
  810. TraceFileFunc(ttidGuiModeSetup);
  811. HRESULT hr = S_OK;
  812. HRESULT hrTmp;
  813. CServiceManager scm;
  814. TraceTag(ttidWizard, "Entering HrWorkstationStart...Checking for LanmanWorkstation presence");
  815. // Open the service control manager
  816. //
  817. hrTmp = scm.HrOpen();
  818. if (SUCCEEDED(hrTmp))
  819. {
  820. // Find the workstation service
  821. //
  822. SC_HANDLE hSvc = OpenService (scm.Handle(),
  823. c_szSvcWorkstation,
  824. SERVICE_QUERY_CONFIG |
  825. SERVICE_QUERY_STATUS |
  826. SERVICE_ENUMERATE_DEPENDENTS |
  827. SERVICE_START | SERVICE_STOP |
  828. SERVICE_USER_DEFINED_CONTROL);
  829. if (hSvc)
  830. {
  831. SERVICE_STATUS status;
  832. const UINT cmsWait = 100;
  833. const UINT cmsTotal = 120000; // 2 minutes
  834. UINT cLoop = cmsTotal / cmsWait;
  835. // Check it's status, exit test once it's running
  836. //
  837. for (UINT nLoop = 0; nLoop < cLoop; nLoop++, Sleep (cmsWait))
  838. {
  839. BOOL fr = QueryServiceStatus (hSvc, &status);
  840. Assert(fr);
  841. if (SERVICE_RUNNING == status.dwCurrentState)
  842. {
  843. break;
  844. }
  845. }
  846. if (SERVICE_RUNNING != status.dwCurrentState)
  847. {
  848. hr = HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE);
  849. #if DBG
  850. OutputDebugString (L"***ERROR*** NETCFG - Workstation service didn't start after more than 2 minutes!\n");
  851. OutputDebugString (L"***ERROR*** NETCFG - Join Domain will fail!\n");
  852. #endif
  853. }
  854. CloseServiceHandle(hSvc);
  855. }
  856. scm.Close();
  857. }
  858. else
  859. {
  860. TraceError("WJOIN.CPP - HrWorkstationStart - Unable to open the Service Manager",hrTmp);
  861. }
  862. TraceError("WJOIN.CPP - HrWorkstationStart",hr);
  863. TraceTag(ttidWizard, "Leaving HrWorkstationStart");
  864. return hr;
  865. }
  866. // Purpose: Secure Domain Join with the new ComputerPassword Answer-File key.
  867. // Domain Join tries to join the domain with the random precreated machine password.
  868. // (the username is not required in this case)
  869. // note: code path is inspired from HrAttemptJoin(JoinData * pData)
  870. // functions defined in ncident.cpp
  871. extern HRESULT HrNetValidateName(IN PCWSTR lpMachine,
  872. IN PCWSTR lpName,
  873. IN PCWSTR lpAccount,
  874. IN PCWSTR lpPassword,
  875. IN NETSETUP_NAME_TYPE NameType);
  876. extern HRESULT HrNetJoinDomain(IN PWSTR lpMachine,
  877. IN PWSTR lpMachineObjectOU,
  878. IN PWSTR lpDomain,
  879. IN PWSTR lpAccount,
  880. IN PWSTR lpPassword,
  881. IN DWORD fJoinOptions);
  882. EXTERN_C HRESULT HrAttemptSecureDomainJoin(JoinData * pData)
  883. {
  884. TraceFileFunc(ttidGuiModeSetup);
  885. HRESULT hr;
  886. Assert(pData);
  887. // 1. wait for the start of LanmanWorkstation service
  888. // 2. check for valid domain
  889. // 3. secure join domain
  890. hr = HrWorkstationStart(pData->hwndDlg);
  891. if (SUCCEEDED(hr))
  892. {
  893. hr = HrNetValidateName(NULL, pData->szDomain , NULL, NULL, NetSetupDomain);
  894. }
  895. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptSecureDomainJoin - HrNetValidateName");
  896. if (SUCCEEDED(hr))
  897. {
  898. // do the secure join domain
  899. DWORD dwJoinOption = 0;
  900. dwJoinOption |= (NETSETUP_JOIN_DOMAIN | NETSETUP_JOIN_UNSECURE | NETSETUP_MACHINE_PWD_PASSED);
  901. if (FInSystemSetup())
  902. {
  903. // During system setup, need to pass special flag that tells join code
  904. // to not do certain operations because SAM is not initialized yet.
  905. dwJoinOption |= NETSETUP_INSTALL_INVOCATION;
  906. }
  907. hr = HrNetJoinDomain(NULL,pData->pszMachineObjectOU,
  908. pData->szDomain, NULL, pData->szComputerPassword,
  909. dwJoinOption);
  910. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptSecureDomainJoin - HrNetJoinDomain");
  911. }
  912. TraceError("HrAttemptSecureDomainJoin", hr);
  913. return hr;
  914. }
  915. EXTERN_C HRESULT HrAttemptJoin(JoinData * pData)
  916. {
  917. TraceFileFunc(ttidGuiModeSetup);
  918. HRESULT hr;
  919. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  920. {
  921. hr = HrWorkstationStart(pData->hwndDlg);
  922. if (SUCCEEDED(hr))
  923. {
  924. hr = pData->pIdent->JoinDomain(pData->szDomain,
  925. pData->pszMachineObjectOU,
  926. pData->szUserName,
  927. pData->szPassword, pData->dwJoinFlag);
  928. }
  929. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - JoinDomain");
  930. }
  931. else
  932. {
  933. // Join a workgroup
  934. hr = pData->pIdent->JoinWorkgroup(pData->szDomain);
  935. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - JoinWorkgroup");
  936. }
  937. if (SUCCEEDED(hr))
  938. {
  939. if (S_OK == pData->pIdent->Validate())
  940. {
  941. hr = pData->pIdent->Apply();
  942. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - Apply");
  943. }
  944. }
  945. if (FAILED(hr))
  946. {
  947. // Rollback any changes
  948. pData->pIdent->Cancel();
  949. }
  950. TraceError("HrAttemptJoin",hr);
  951. return hr;
  952. }
  953. HRESULT HrAttemptJoin(JoinData * pData, DWORD dwRetries, DWORD dwDelayPeriod)
  954. {
  955. HRESULT hr = E_FAIL;
  956. DWORD dwCount = dwRetries;
  957. do
  958. {
  959. hr = HrAttemptJoin(pData);
  960. if (FAILED(hr))
  961. {
  962. dwCount--;
  963. Sleep(dwDelayPeriod);
  964. }
  965. } while (FAILED(hr) && (dwCount));
  966. return hr;
  967. }
  968. HRESULT HrAttemptSecureDomainJoin(JoinData * pData, DWORD dwRetries, DWORD dwDelayPeriod)
  969. {
  970. HRESULT hr = E_FAIL;
  971. DWORD dwCount = dwRetries;
  972. do
  973. {
  974. hr = HrAttemptSecureDomainJoin(pData);
  975. if (FAILED(hr))
  976. {
  977. dwCount--;
  978. Sleep(dwDelayPeriod);
  979. }
  980. } while (FAILED(hr) && (dwCount));
  981. return hr;
  982. }
  983. EXTERN_C DWORD JoinDomainWorkThrd(JoinData * pData)
  984. {
  985. TraceFileFunc(ttidGuiModeSetup);
  986. BOOL fUninitCOM = FALSE;
  987. HRESULT hr;
  988. Assert(NULL != pData);
  989. CWizard * pWizard =
  990. reinterpret_cast<CWizard *>(::GetWindowLongPtr(pData->hwndDlg, DWLP_USER));
  991. Assert(NULL != pWizard);
  992. // Initialize COM on this thread
  993. //
  994. hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
  995. if (FAILED(hr))
  996. {
  997. // $REVIEW - LogError ?
  998. TraceTag(ttidWizard, "Failed to initialize COM join work thread");
  999. goto Done;
  1000. }
  1001. else
  1002. {
  1003. // Remember to uninitialize COM on thread exit
  1004. fUninitCOM = TRUE;
  1005. }
  1006. DWORD dwNumTries = 1;
  1007. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  1008. {
  1009. Sleep(c_dwDomainJoinWaitDelay);
  1010. dwNumTries = 5;
  1011. }
  1012. if (pData->dwJoinFlag & JDF_MACHINE_PWD_PASSED)
  1013. { // Unattended Answer-File specified with ComputerPassword key
  1014. // Try Secure Domain Join
  1015. TraceTag(ttidWizard, "Attempting join with precreated computer password.");
  1016. hr = HrAttemptSecureDomainJoin(pData, dwNumTries, c_dwDomainJoinWaitDelay);
  1017. if (FAILED(hr))
  1018. {
  1019. // clear the secure domain join flag and try the normal join
  1020. TraceTag(ttidWizard, "Failed in secure join domain.");
  1021. pData->dwJoinFlag &= ~JDF_MACHINE_PWD_PASSED;
  1022. }
  1023. else
  1024. goto Cleanup;
  1025. }
  1026. // Try the normal join
  1027. //
  1028. TraceTag(ttidWizard, "Attempting join WITHOUT trying to create an account.");
  1029. hr = HrAttemptJoin(pData, 3, 10000);
  1030. // If the join failed, and the Create Account flag has not been
  1031. // specified, try adding it and reattempt the join.
  1032. //
  1033. if (FAILED(hr) && !(pData->dwJoinFlag & JDF_CREATE_ACCOUNT))
  1034. {
  1035. // Clear the Unsecure join flag if set, creating an account is
  1036. // mutually exclusive. Set the create account flag to reattempt.
  1037. //
  1038. pData->dwJoinFlag &= ~JDF_JOIN_UNSECURE;
  1039. pData->dwJoinFlag |= JDF_CREATE_ACCOUNT;
  1040. TraceTag(ttidWizard, "Attempting join but trying to create an account.");
  1041. hr = HrAttemptJoin(pData, dwNumTries, c_dwDomainJoinWaitDelay);
  1042. }
  1043. Cleanup:
  1044. // Cleanup username/password and MachineObjectOU
  1045. //
  1046. pData->szUserName[0] = 0;
  1047. pData->szPassword[0] = 0;
  1048. pData->dwJoinFlag = 0;
  1049. MemFree(pData->pszMachineObjectOU);
  1050. pData->pszMachineObjectOU = NULL;
  1051. pData->szComputerPassword[0] = 0;
  1052. if (FAILED(hr))
  1053. {
  1054. // Raid 380374: no UI allowed if in full unattended mode ..
  1055. if (UM_FULLUNATTENDED == pWizard->GetUnattendedMode())
  1056. {
  1057. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  1058. {
  1059. NetSetupLogStatusV( LogSevError,
  1060. SzLoadIds (IDS_E_UNATTENDED_JOIN_DOMAIN),
  1061. pData->szDomain);
  1062. }
  1063. else
  1064. {
  1065. NetSetupLogStatusV( LogSevError,
  1066. SzLoadIds (IDS_E_UNATTENDED_JOIN_WORKGROUP),
  1067. pData->szDomain);
  1068. }
  1069. // proceed to the exit page
  1070. PostMessage(pData->hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1071. }
  1072. else
  1073. {
  1074. // If we're in unattended mode, consider it failed.
  1075. pData->fUnattendedFailed = TRUE;
  1076. PostMessage(pData->hwndDlg, PWM_JOINFAILURE, 0, (LPARAM)hr);
  1077. }
  1078. }
  1079. else
  1080. {
  1081. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  1082. NotifyPostSetupWizard(PSW_JOINEDDOMAIN, pWizard);
  1083. PostMessage(pData->hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1084. }
  1085. Done:
  1086. // Uninitialize COM for this thread
  1087. //
  1088. if (fUninitCOM)
  1089. {
  1090. CoUninitialize();
  1091. }
  1092. TraceTag(ttidWizard, "Leaving JoinDomainWorkThrd...");
  1093. return( 0 );
  1094. }
  1095. //
  1096. // Function: HrJoinProcessAnswerFile
  1097. //
  1098. // Purpose: Read the answer file and populate the in memory structures
  1099. // and UI with the data found
  1100. //
  1101. // Parameters:
  1102. //
  1103. // Returns: HRESULT, S_OK on success
  1104. // S_FALSE if required information is missing
  1105. // A failed error code on error
  1106. //
  1107. HRESULT HrJoinProcessAnswerFile(HWND hwndDlg, CWizard * pWizard,
  1108. JoinData * pData)
  1109. {
  1110. TraceFileFunc(ttidGuiModeSetup);
  1111. CSetupInfFile csif;
  1112. INFCONTEXT ctx;
  1113. BOOL fValue;
  1114. HRESULT hr;
  1115. int nId = BTN_JOIN_WORKGROUP;
  1116. tstring str;
  1117. pData->dwJoinFlag = 0;
  1118. pData->szDomain[0] = 0;
  1119. pData->szUserName[0] = 0;
  1120. pData->szPassword[0] = 0;
  1121. pData->pszMachineObjectOU = NULL;
  1122. pData->szComputerPassword[0] = 0;
  1123. if ((NULL == pWizard->PSetupData()) ||
  1124. (NULL == pWizard->PSetupData()->UnattendFile))
  1125. {
  1126. hr = NETSETUP_E_ANS_FILE_ERROR;
  1127. goto Error;
  1128. }
  1129. // Open the answerfile
  1130. //
  1131. hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile,
  1132. NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
  1133. if (FAILED(hr))
  1134. {
  1135. hr = S_FALSE;
  1136. TraceTag(ttidWizard, "Unable to open answer file!!!");
  1137. goto Error;
  1138. }
  1139. // Check for existance of the identification section. If it is not
  1140. // present return S_FALSE to indicate identification info not supplied
  1141. //
  1142. hr = HrSetupFindFirstLine (csif.Hinf(), c_szAfSectionIdentification,
  1143. NULL, &ctx);
  1144. if (SPAPI_E_LINE_NOT_FOUND == hr)
  1145. {
  1146. hr = S_FALSE;
  1147. goto Error;
  1148. }
  1149. // Try to get the workgroup
  1150. //
  1151. hr = csif.HrGetString(c_szAfSectionIdentification,
  1152. c_szAfJoinWorkgroup, &str);
  1153. if (SUCCEEDED(hr) && str.length())
  1154. {
  1155. if (MAX_WORKGROUPNAME_LENGTH >= str.length())
  1156. {
  1157. wcscpy(pData->szDomain, str.c_str());
  1158. TraceTag(ttidWizard, "Joining workgroup: %S", pData->szDomain);
  1159. }
  1160. else
  1161. {
  1162. hr = NETSETUP_E_ANS_FILE_ERROR;
  1163. TraceTag(ttidWizard, "JOIN Workgroup - Invalid workgroup supplied.");
  1164. goto Error;
  1165. }
  1166. }
  1167. else
  1168. {
  1169. // Try to get the domain
  1170. //
  1171. hr = csif.HrGetString(c_szAfSectionIdentification,
  1172. c_szAfJoinDomain, &str);
  1173. if (SPAPI_E_LINE_NOT_FOUND == hr)
  1174. {
  1175. // No domain or workgroup entry, skip joining a domain
  1176. hr = S_FALSE;
  1177. goto Error;
  1178. }
  1179. else if (FAILED(hr) || (0 == str.length()) ||
  1180. (MAX_DOMAINNAME_LENGTH < str.length()))
  1181. {
  1182. hr = NETSETUP_E_ANS_FILE_ERROR;
  1183. TraceTag(ttidWizard, "JOIN Domain - Invalid domain supplied.");
  1184. goto Error;
  1185. }
  1186. // Joining a domain...
  1187. //
  1188. nId = BTN_JOIN_DOMAIN;
  1189. wcscpy(pData->szDomain, str.c_str());
  1190. TraceTag(ttidWizard, "Joining domain: %S", pData->szDomain);
  1191. // If we're upgrading from win9x add the special flag
  1192. //
  1193. hr = csif.HrGetString(c_szAfSectionNetworking,
  1194. c_szAfUpgradeFromProduct, &str);
  1195. if (SUCCEEDED(hr) &&
  1196. (0 == lstrcmpiW(str.c_str(), c_szAfWin95)))
  1197. {
  1198. pData->dwJoinFlag |= JDF_WIN9x_UPGRADE;
  1199. }
  1200. // Support unsecure joins
  1201. //
  1202. hr = csif.HrGetStringAsBool(c_szAfSectionIdentification,
  1203. c_szAfUnsecureJoin,
  1204. &fValue);
  1205. if (SUCCEEDED(hr) && fValue)
  1206. {
  1207. pData->dwJoinFlag |= JDF_JOIN_UNSECURE;
  1208. }
  1209. // Is a MachineObjectOU specified?
  1210. //
  1211. hr = csif.HrGetString(c_szAfSectionIdentification,
  1212. c_szAfMachineObjectOU, &str);
  1213. if (SUCCEEDED(hr) && str.length())
  1214. {
  1215. pData->pszMachineObjectOU = reinterpret_cast<WCHAR *>(MemAlloc(sizeof(WCHAR) * (str.length() + 1)));
  1216. if (pData->pszMachineObjectOU)
  1217. {
  1218. TraceTag(ttidWizard, "Machine Object OU: %S", pData->szDomain);
  1219. lstrcpyW(pData->pszMachineObjectOU, str.c_str());
  1220. }
  1221. }
  1222. // Bug# 204377 secure domain join shouldn't require both "ComputerPassword" and
  1223. // "DomainAdmin"/"DomainAdminPassword" options to be presence
  1224. // in Answer-File simultaneously.
  1225. //
  1226. // Bug# 204378 If the Answer-File specifies the "ComputerPassword" key
  1227. // in the "Indentification" section, the code should attempt
  1228. // a secure domain join, regardless of the presence of the
  1229. // "DoOldstyleDomainjoin" key
  1230. // check if this is a secure domain join by
  1231. // quering the random machine account password
  1232. hr = csif.HrGetString(c_szAfSectionIdentification,
  1233. c_szAfComputerPassword, &str);
  1234. if (SUCCEEDED(hr) && (str.length() <= SAM_MAX_PASSWORD_LENGTH) && str.length())
  1235. {
  1236. TraceTag(ttidWizard, "Got the value of ComputerPassword");
  1237. wcscpy(pData->szComputerPassword, str.c_str());
  1238. // signal that we need to try secure domain join by
  1239. // passing the random machine password
  1240. pData->dwJoinFlag |= JDF_MACHINE_PWD_PASSED;
  1241. // Bug# 204378, make sure we won't do the unsecure domain join
  1242. // and we'll try to read "DomainAdmin"/"DomainAdminPassword"
  1243. // in the following if block.
  1244. pData->dwJoinFlag &= ~JDF_JOIN_UNSECURE;
  1245. }
  1246. // If not a remote boot client, Query the username, unless this
  1247. // is an unsecure domain join which doesn't need username/password.
  1248. //
  1249. if (
  1250. #if defined(REMOTE_BOOT)
  1251. (S_FALSE == HrIsRemoteBootMachine()) ||
  1252. #endif // defined(REMOTE_BOOT)
  1253. ((pData->dwJoinFlag & JDF_JOIN_UNSECURE) == 0))
  1254. {
  1255. hr = csif.HrGetString(c_szAfSectionIdentification,
  1256. c_szAfDomainAdmin, &str);
  1257. if (SUCCEEDED(hr) && (MAX_USERNAME_LENGTH > str.length()))
  1258. {
  1259. wcscpy(pData->szUserName, str.c_str());
  1260. // Query the password
  1261. //
  1262. hr = csif.HrGetString(c_szAfSectionIdentification,
  1263. c_szAfDomainAdminPassword, &str);
  1264. if (SUCCEEDED(hr) && (SAM_MAX_PASSWORD_LENGTH > str.length()))
  1265. {
  1266. wcscpy(pData->szPassword, str.c_str());
  1267. // Raid 195920 - If both username and password are
  1268. // present, treat this like a fresh install and DO NOT
  1269. // use the JDF_WIN9x_UPGRADE flag
  1270. pData->dwJoinFlag &= ~(JDF_WIN9x_UPGRADE | JDF_JOIN_UNSECURE);
  1271. }
  1272. }
  1273. // Bug# 204377
  1274. // ignore any error on reading of "DomainAdmin"/"DomainAdminPassword" keys
  1275. // if "ComputerPassword" has been specified.
  1276. if (! (pData->dwJoinFlag & JDF_MACHINE_PWD_PASSED) )
  1277. {
  1278. // If failed or either is longer than the maximum length return an error
  1279. //
  1280. if (FAILED(hr) || (MAX_USERNAME_LENGTH <= str.length()))
  1281. {
  1282. hr = NETSETUP_E_ANS_FILE_ERROR;
  1283. TraceTag(ttidWizard, "JOIN Domain - Invalid username/password supplied.");
  1284. goto Error;
  1285. }
  1286. }
  1287. }
  1288. }
  1289. // Normalize any optional errors
  1290. //
  1291. hr = S_OK;
  1292. Error:
  1293. // Update the UI and pData with the info we managed to read.
  1294. //
  1295. CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, nId);
  1296. SetWindowText(GetDlgItem(hwndDlg, nId == BTN_JOIN_DOMAIN ? EDT_DOMAINJOIN_NAME : EDT_WORKGROUPJOIN_NAME ), pData->szDomain);
  1297. JoinUpdatePromptText(hwndDlg);
  1298. TraceHr(ttidWizard, FAL, hr, FALSE, "HrJoinProcessAnswerFile");
  1299. return hr;
  1300. }
  1301. //
  1302. // Function: OnJoinDoUnattended
  1303. //
  1304. // Purpose:
  1305. //
  1306. // Parameters:
  1307. //
  1308. // Returns: BOOL
  1309. //
  1310. BOOL OnJoinDoUnattended(HWND hwndDlg)
  1311. {
  1312. TraceFileFunc(ttidGuiModeSetup);
  1313. DWORD dwThreadId = 0;
  1314. HRESULT hr = S_OK;
  1315. CWizard * pWizard =
  1316. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  1317. Assert(NULL != pWizard);
  1318. JoinData * pData = reinterpret_cast<JoinData *>
  1319. (pWizard->GetPageData(IDD_Join));
  1320. Assert(NULL != pData);
  1321. Assert(NULL != pData->pIdent);
  1322. Assert(NULL != pData->hwndDlg);
  1323. if(pData) {
  1324. // Create the unattended thread
  1325. //
  1326. HANDLE hthrd = CreateThread(NULL, STACK_SIZE_TINY,
  1327. (LPTHREAD_START_ROUTINE)JoinDomainWorkThrd,
  1328. (LPVOID)pData, 0, &dwThreadId);
  1329. if (NULL != hthrd)
  1330. {
  1331. // Set the wait cursor
  1332. //
  1333. SetCursorToHourglass( hwndDlg, pData );
  1334. // Disable all the controls
  1335. //
  1336. PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
  1337. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
  1338. CloseHandle(hthrd);
  1339. }
  1340. else
  1341. {
  1342. hr = HrFromLastWin32Error();
  1343. }
  1344. }
  1345. TraceHr(ttidWizard, FAL, hr, FALSE, "OnJoinDoUnattended");
  1346. return (SUCCEEDED(hr));
  1347. }
  1348. // Determines whether the computer DNS domain name should be kept in sync with
  1349. // the DNS domain name of the domain to which it is joined. If no
  1350. // synchonization is required (unlikely, as sync is the default), do nothing.
  1351. //
  1352. // Otherwise, attempt to determine the DNS domain name of the domain and write
  1353. // this value into reg key. If the DNS domain name cannot be determined,
  1354. // write a flag to a separate reg key to indicate that whatever services that
  1355. // care about the computer DNS domain name (i.e. kerberos authentication)
  1356. // should try to fix up the name.
  1357. void
  1358. fixupComputerDNSDomainName()
  1359. {
  1360. TraceFileFunc(ttidGuiModeSetup);
  1361. TraceTag(ttidWizard, "Entering fixupComputerDNSDomainName");
  1362. // check a reg key for the sync flag.
  1363. bool fSetName = false;
  1364. HKEY hkeyParams = 0;
  1365. HRESULT hr =
  1366. HrRegOpenKeyEx(
  1367. HKEY_LOCAL_MACHINE,
  1368. c_szIpParameters,
  1369. // all access as we may need to write a value here if we fail.
  1370. KEY_READ_WRITE,
  1371. &hkeyParams);
  1372. if (SUCCEEDED(hr))
  1373. {
  1374. DWORD dwValue = 0;
  1375. hr = HrRegQueryDword(hkeyParams, c_szSyncDomainWithMembership, &dwValue);
  1376. if (SUCCEEDED(hr) && (1 == dwValue))
  1377. {
  1378. // the sync flag was present in the registry, and its value is true
  1379. fSetName = true;
  1380. }
  1381. else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
  1382. {
  1383. // the sync flag was not present in the registry, which means we
  1384. // assume the default value of true.
  1385. fSetName = true;
  1386. hr = S_OK;
  1387. }
  1388. }
  1389. if (SUCCEEDED(hr) && fSetName)
  1390. {
  1391. bool fixup_success = false;
  1392. // 293301 Here we attempt to determine the DNS domain name of the domain
  1393. // the machine is joined to. If we cannot determine that name (because
  1394. // a dc could not be located, for example), then we will set a flag in
  1395. // the registry so that kerberos authentication agents will come along
  1396. // and fixup the DNS domain name.
  1397. PDOMAIN_CONTROLLER_INFO pDCInfo = 0;
  1398. DWORD dw =
  1399. DsGetDcName(
  1400. 0,
  1401. 0,
  1402. 0,
  1403. 0,
  1404. // make sure to ask for the DNS domain name, otherwise we're
  1405. // likely to get the flatname
  1406. DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME,
  1407. &pDCInfo);
  1408. if (NOERROR == dw)
  1409. {
  1410. Assert(pDCInfo->DomainName);
  1411. Assert(pDCInfo->Flags & DS_DNS_DOMAIN_FLAG);
  1412. TraceTag(ttidWizard, "DsGetDcName succeeded %s", pDCInfo->DomainName);
  1413. if (pDCInfo->Flags & DS_DNS_DOMAIN_FLAG)
  1414. {
  1415. // the domain name is indeed the DNS name
  1416. // chop off any trailing '.'
  1417. WCHAR* AbsoluteSignifier =
  1418. &pDCInfo->DomainName[ wcslen(pDCInfo->DomainName) - 1 ];
  1419. if (*AbsoluteSignifier == L'.')
  1420. {
  1421. *AbsoluteSignifier = 0;
  1422. }
  1423. // set the computer's DNS domain name.
  1424. if (
  1425. SetComputerNameEx(
  1426. ComputerNamePhysicalDnsDomain,
  1427. pDCInfo->DomainName) )
  1428. {
  1429. fixup_success = true;
  1430. }
  1431. #if DBG
  1432. else
  1433. {
  1434. // this just isn't our day.
  1435. TraceTag(ttidWizard, "SetComputerNameEx failed");
  1436. }
  1437. #endif
  1438. }
  1439. NetApiBufferFree(pDCInfo);
  1440. }
  1441. #if DBG
  1442. else
  1443. {
  1444. TraceTag(ttidWizard, "DsGetDcName returned %ld",dw);
  1445. }
  1446. #endif
  1447. // at this point, fixup_success will indicate whether we successfully
  1448. // set the computer's domain DNS name, or not. If not, then we need
  1449. // to write a flag into the registry so that kerberos auth will fix
  1450. // it up later.
  1451. if (!fixup_success)
  1452. {
  1453. // write a flag to have someone else do the fixup
  1454. HrRegSetDword(hkeyParams, L"DoDNSDomainFixup", 1);
  1455. }
  1456. }
  1457. RegCloseKey(hkeyParams);
  1458. }
  1459. //
  1460. // Function: JoinUpgradeNT351orNT4toNT5
  1461. //
  1462. // Purpose: If currently processing NT4 -> NT5 upgrade, set the
  1463. // computer name.
  1464. //
  1465. // Parameters:
  1466. //
  1467. // Returns: none
  1468. //
  1469. VOID JoinUpgradeNT351orNT4toNT5(CWizard * pWizard, JoinData * pData)
  1470. {
  1471. TraceFileFunc(ttidGuiModeSetup);
  1472. HRESULT hr = S_OK;
  1473. CSetupInfFile csif;
  1474. INFCONTEXT ctx;
  1475. TraceTag(ttidWizard, "Checking for the need to do NT4->NT5 Join conversions...");
  1476. // If unattended
  1477. //
  1478. if (IsUnattended(pWizard) && (NULL != pWizard->PSetupData()) &&
  1479. (NULL != pWizard->PSetupData()->UnattendFile))
  1480. {
  1481. hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile,
  1482. NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
  1483. if (SUCCEEDED(hr))
  1484. {
  1485. DWORD dw;
  1486. hr = csif.HrGetDword(c_szAfSectionNetworking, c_szAfBuildNumber, &dw);
  1487. if (SUCCEEDED(hr) && ((wWinNT4BuildNumber == dw) ||
  1488. (wWinNT351BuildNumber == dw)))
  1489. {
  1490. hr = pData->pIdent->GetComputerRole(&dw);
  1491. if (SUCCEEDED(hr) && (dw == GCR_MEMBER))
  1492. {
  1493. //fixupComputerDNSDomainName();
  1494. NC_TRY
  1495. {
  1496. TraceTag (ttidWizard, "Calling NetpUpgradePreNT5JoinInfo...");
  1497. NetpUpgradePreNT5JoinInfo ();
  1498. }
  1499. NC_CATCH_ALL
  1500. {
  1501. TraceHr (
  1502. ttidWizard, FAL, E_FAIL, FALSE,
  1503. "NetpUpgradePreNT5JoinInfo failed. "
  1504. "Likely delay load problem with netapi32.dll");
  1505. }
  1506. }
  1507. }
  1508. }
  1509. }
  1510. TraceHr(ttidWizard, FAL, hr, FALSE, "JoinUpgradeNT351orNT4toNT5");
  1511. }
  1512. //
  1513. // Function: OnJoinPageActivate
  1514. //
  1515. // Purpose:
  1516. //
  1517. // Parameters:
  1518. //
  1519. // Returns:
  1520. //
  1521. BOOL OnJoinPageActivate(HWND hwndDlg)
  1522. {
  1523. TraceFileFunc(ttidGuiModeSetup);
  1524. // Retrieve the CWizard instance from the dialog
  1525. CWizard * pWizard =
  1526. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  1527. Assert(NULL != pWizard);
  1528. JoinData * pData = reinterpret_cast<JoinData *>
  1529. (pWizard->GetPageData(IDD_Join));
  1530. Assert(NULL != pData);
  1531. TraceTag(ttidWizard, "Entering Join page...");
  1532. if (ISDC(ProductType(pWizard)))
  1533. {
  1534. // 412142 : we are going to skip the Join page a little farther down, but
  1535. // even for a DC, we need to do this.
  1536. //
  1537. if (FALSE == pData->fUpgraded)
  1538. {
  1539. JoinUpgradeNT351orNT4toNT5(pWizard, pData);
  1540. pData->fUpgraded = TRUE;
  1541. }
  1542. }
  1543. // mbend 02/08/2000
  1544. //
  1545. // BUG 433915
  1546. // Domain/Workgroup page in setup: Check to by-pass for SBS case
  1547. // If this computer is a domain controller or there are no adapters or we don't
  1548. // want activation, then don't show the Join page.
  1549. //
  1550. if (IsSBS() || ISDC(ProductType(pWizard)) || !pWizard->PAdapterQueue()->FAdaptersInstalled() ||
  1551. (IsRunningOnPersonal() && !(IsUnattended(pWizard) && (FALSE == pData->fUpgraded))) )
  1552. {
  1553. PAGEDIRECTION PageDir = pWizard->GetPageDirection(IDD_Join);
  1554. if (NWPD_FORWARD == PageDir)
  1555. {
  1556. // if forward goto exit page
  1557. //
  1558. pWizard->SetPageDirection(IDD_Join, NWPD_BACKWARD);
  1559. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_Exit);
  1560. }
  1561. else
  1562. {
  1563. // if backward goto upgrade page
  1564. //
  1565. pWizard->SetPageDirection(IDD_Join, NWPD_FORWARD);
  1566. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_Upgrade);
  1567. }
  1568. }
  1569. else // !DC
  1570. {
  1571. // Accept focus
  1572. //
  1573. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 0);
  1574. PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
  1575. // If we're in unattended mode and haven't tried to join a
  1576. // domain/workgroup then give it a try
  1577. //
  1578. if (IsUnattended(pWizard) && (FALSE == pData->fUpgraded))
  1579. {
  1580. HRESULT hr;
  1581. pData->fUpgraded = TRUE;
  1582. // Raid 193450 - NT4 -> NT5 we need to SetComputerNameEx
  1583. //
  1584. JoinUpgradeNT351orNT4toNT5(pWizard, pData);
  1585. // Get the join parameters from the answer file and populate the UI
  1586. //
  1587. hr = HrJoinProcessAnswerFile(hwndDlg, pWizard, pData);
  1588. if (S_FALSE == hr)
  1589. {
  1590. // S_FALSE was returned, this means no answerfile section
  1591. // for identification is present or not all the required
  1592. // data is present. Advance without joining unless we're in
  1593. // defaulthide mode.
  1594. // Advance regardless if we're upgrading, or running on Personal.
  1595. //
  1596. Assert(S_FALSE == hr);
  1597. if (((UM_DEFAULTHIDE == pWizard->GetUnattendedMode()) ||
  1598. (UM_READONLY == pWizard->GetUnattendedMode()))
  1599. && (!IsUpgrade(pWizard))
  1600. && (!IsRunningOnPersonal()) )
  1601. {
  1602. // Pretend something failed so that pressing NEXT will
  1603. // do the join. Basically we just populated the UI from
  1604. // the answerfile.
  1605. //
  1606. pData->fUnattendedFailed = TRUE;
  1607. }
  1608. else
  1609. {
  1610. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1611. }
  1612. }
  1613. else
  1614. {
  1615. if (FAILED(hr))
  1616. {
  1617. if (UM_FULLUNATTENDED == pWizard->GetUnattendedMode())
  1618. {
  1619. // Raid 380374: no UI allowed if in full unattended mode ..
  1620. NetSetupLogStatusV( LogSevError,
  1621. SzLoadIds (IDS_E_UNATTENDED_INVALID_ID_SECTION));
  1622. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1623. }
  1624. else
  1625. {
  1626. // Stop on this page, something was wrong in the answerfile
  1627. //
  1628. pData->fUnattendedFailed = TRUE;
  1629. }
  1630. }
  1631. else
  1632. {
  1633. // If we're in UM_FULLUNATTENDED or in UM_DEFAULTHIDE mode
  1634. // launch the thread which will do the unattended join
  1635. //
  1636. Assert(S_OK == hr);
  1637. if ((UM_FULLUNATTENDED == pWizard->GetUnattendedMode()) ||
  1638. (UM_DEFAULTHIDE == pWizard->GetUnattendedMode()) ||
  1639. (UM_READONLY == pWizard->GetUnattendedMode()))
  1640. {
  1641. OnJoinDoUnattended(hwndDlg);
  1642. }
  1643. else
  1644. {
  1645. // Pretend something failed so that pressing NEXT will
  1646. // do the join. Basically we just populated the UI from
  1647. // the answerfile.
  1648. //
  1649. pData->fUnattendedFailed = TRUE;
  1650. }
  1651. }
  1652. }
  1653. }
  1654. // If something failed in the unattended case or
  1655. // If we are not unattended and we did not process this,
  1656. // make sure the page shows if called from GUI mode setup.
  1657. if ( pData->fUnattendedFailed ||
  1658. !IsUnattended(pWizard) )
  1659. {
  1660. // Make the page visible if it is not an upgrade, otherwise just continue to the next page.
  1661. if (!IsUpgrade(pWizard))
  1662. {
  1663. if (g_pSetupWizard != NULL)
  1664. {
  1665. g_pSetupWizard->PSetupData()->ShowHideWizardPage(TRUE);
  1666. }
  1667. UpdateNextBackBttns(hwndDlg);
  1668. }
  1669. else
  1670. {
  1671. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1672. }
  1673. }
  1674. }
  1675. return TRUE;
  1676. }
  1677. //
  1678. // Function: OnJoinInitDialog
  1679. //
  1680. // Purpose:
  1681. //
  1682. // Parameters:
  1683. //
  1684. // Returns:
  1685. //
  1686. BOOL OnJoinInitDialog(HWND hwndDlg, LPARAM lParam)
  1687. {
  1688. TraceFileFunc(ttidGuiModeSetup);
  1689. HRESULT hr;
  1690. CWizard * pWizard;
  1691. JoinData * pData;
  1692. PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
  1693. Assert(psp->lParam);
  1694. ::SetWindowLongPtr(hwndDlg, DWLP_USER, psp->lParam);
  1695. pWizard = reinterpret_cast<CWizard *>(psp->lParam);
  1696. Assert(NULL != pWizard);
  1697. pData = reinterpret_cast<JoinData *>(pWizard->GetPageData(IDD_Join));
  1698. Assert(NULL != pData);
  1699. if(!pData)
  1700. {
  1701. return false;
  1702. }
  1703. // Set the descriptive text
  1704. tstring str = SzLoadIds(IDS_TXT_JOIN_DESC_1);
  1705. str += L"\n";
  1706. str += SzLoadIds(IDS_TXT_JOIN_DESC_2);
  1707. SetWindowText(GetDlgItem(hwndDlg, TXT_JOIN_DESC), str.c_str());
  1708. // Set the maximum length of text in the edit control, and
  1709. // subclass it so when the control has no text the next bttn
  1710. // is disabled.
  1711. HWND hwndEditDomain = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME);
  1712. SendMessage(hwndEditDomain, EM_LIMITTEXT, MAX_DOMAINNAME_LENGTH, 0L);
  1713. ::SetWindowLongPtr(hwndEditDomain, GWLP_USERDATA, ::GetWindowLongPtr(hwndEditDomain, GWLP_WNDPROC));
  1714. ::SetWindowLongPtr(hwndEditDomain, GWLP_WNDPROC, (LONG_PTR)JoinEditSubclassProc);
  1715. HWND hwndEditWorkgroup = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME);
  1716. SendMessage(hwndEditWorkgroup, EM_LIMITTEXT, MAX_WORKGROUPNAME_LENGTH, 0L);
  1717. ::SetWindowLongPtr(hwndEditWorkgroup, GWLP_USERDATA, ::GetWindowLongPtr(hwndEditWorkgroup, GWLP_WNDPROC));
  1718. ::SetWindowLongPtr(hwndEditWorkgroup, GWLP_WNDPROC, (LONG_PTR)JoinEditSubclassProc);
  1719. pData->hwndDlg = hwndDlg;
  1720. // Initialize to Workgroup default
  1721. CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP,
  1722. BTN_JOIN_DOMAIN, BTN_JOIN_WORKGROUP);
  1723. // Get the identification interface
  1724. TraceTag(ttidWizard, "Querying computer role...");
  1725. hr = HrGetIdentInterface(&pData->pIdent);
  1726. if (FAILED(hr))
  1727. {
  1728. Assert(NULL == pData->pIdent);
  1729. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
  1730. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
  1731. }
  1732. else
  1733. {
  1734. // Update the UI based on the selection
  1735. UpdateJoinUsingComputerRole(hwndDlg, pWizard);
  1736. }
  1737. return FALSE;
  1738. }
  1739. //
  1740. // Function: OnJoinWizBack
  1741. //
  1742. // Purpose:
  1743. //
  1744. // Parameters:
  1745. //
  1746. // Returns:
  1747. //
  1748. BOOL OnJoinWizBack(HWND hwndDlg)
  1749. {
  1750. TraceFileFunc(ttidGuiModeSetup);
  1751. OnProcessPrevAdapterPagePrev(hwndDlg, IDD_Upgrade);
  1752. return TRUE;
  1753. }
  1754. //
  1755. // Function: GetCredentials
  1756. //
  1757. // Purpose: Prompt the user for username and password.
  1758. //
  1759. // Returns: TRUE on success, otherwise FALSE.
  1760. //
  1761. // Author: asinha 5/03/2001
  1762. //
  1763. BOOL GetCredentials (HWND hwndParent, JoinData *pData)
  1764. {
  1765. WCHAR szCaption[CREDUI_MAX_CAPTION_LENGTH+1];
  1766. CREDUI_INFOW uiInfo;
  1767. DWORD dwErr;
  1768. TraceFileFunc(ttidGuiModeSetup);
  1769. DwFormatString(SzLoadIds(IDS_JOIN_DOMAIN_CAPTION), szCaption,
  1770. celems(szCaption), pData->szDomain);
  1771. ZeroMemory( &uiInfo, sizeof(uiInfo) );
  1772. uiInfo.cbSize = sizeof(CREDUI_INFOW);
  1773. uiInfo.hwndParent = hwndParent;
  1774. uiInfo.pszMessageText = SzLoadIds(IDS_JOIN_DOMAIN_TEXT);
  1775. uiInfo.pszCaptionText = szCaption;
  1776. dwErr = CredUIPromptForCredentialsW(
  1777. &uiInfo,
  1778. NULL,
  1779. NULL,
  1780. NO_ERROR,
  1781. pData->szUserName,
  1782. MAX_USERNAME_LENGTH+1,
  1783. pData->szPassword,
  1784. SAM_MAX_PASSWORD_LENGTH+1,
  1785. NULL,
  1786. CREDUI_FLAGS_DO_NOT_PERSIST |
  1787. CREDUI_FLAGS_GENERIC_CREDENTIALS |
  1788. CREDUI_FLAGS_VALIDATE_USERNAME |
  1789. CREDUI_FLAGS_COMPLETE_USERNAME |
  1790. CREDUI_FLAGS_ALWAYS_SHOW_UI );
  1791. Assert(dwErr != ERROR_INVALID_PARAMETER);
  1792. Assert(dwErr != ERROR_INVALID_FLAGS);
  1793. if (dwErr == ERROR_CANCELLED)
  1794. {
  1795. pData->szUserName[0] = 0;
  1796. pData->szPassword[0] = 0;
  1797. }
  1798. return NO_ERROR == dwErr; // e.g. ERROR_CANCELLED
  1799. }
  1800. //
  1801. // Function: JoinWorkgroupDomain
  1802. //
  1803. // Purpose:
  1804. //
  1805. // Parameters:
  1806. //
  1807. // Returns: nothing
  1808. //
  1809. VOID JoinWorkgroupDomain(HWND hwndDlg, CWizard * pWizard,
  1810. JoinData * pData)
  1811. {
  1812. TraceFileFunc(ttidGuiModeSetup);
  1813. DWORD dwThreadId = 0;
  1814. HANDLE hthrd = NULL;
  1815. Assert(NULL != pData->pIdent);
  1816. Assert(NULL != pData->hwndDlg);
  1817. // Retain the domain/workgroup name. Note that the answerfile
  1818. // populates the UI before this routine is called so requerying
  1819. // from the UI covers both cases (answerfile or user input)
  1820. //
  1821. if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN))
  1822. {
  1823. HWND hwndEdit = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME);
  1824. Assert(0 != GetWindowTextLength(hwndEdit));
  1825. GetWindowText(hwndEdit, pData->szDomain, MAX_DOMAINNAME_LENGTH + 1);
  1826. // If no information was seeded into the structure, prompt for it.
  1827. //
  1828. if (0 == pData->szUserName[0])
  1829. {
  1830. pData->szPassword[0] = 0;
  1831. // Preserve the only Win9x upgrade flag if it was present
  1832. //
  1833. pData->dwJoinFlag &= JDF_WIN9x_UPGRADE;
  1834. // Get the username/password when joining a domain
  1835. //
  1836. BOOL bRet;
  1837. bRet = GetCredentials(GetParent(hwndDlg), pData);
  1838. // Note: Must set return as -1 otherwise join page will advance
  1839. //
  1840. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
  1841. if (bRet == FALSE)
  1842. {
  1843. return;
  1844. }
  1845. }
  1846. }
  1847. else
  1848. {
  1849. HWND hwndEdit = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME);
  1850. Assert(0 != GetWindowTextLength(hwndEdit));
  1851. GetWindowText(hwndEdit, pData->szDomain, MAX_WORKGROUPNAME_LENGTH + 1);
  1852. // Initialize the workstation settings for username/password
  1853. // and join flags
  1854. //
  1855. pData->szUserName[0] = 0;
  1856. pData->dwJoinFlag = 0;
  1857. pData->szPassword[0] = 0;
  1858. MemFree(pData->pszMachineObjectOU);
  1859. pData->pszMachineObjectOU = NULL;
  1860. pData->szComputerPassword[0] = 0;
  1861. }
  1862. // Create the thread to join the workgroup/domain
  1863. hthrd = CreateThread(NULL, STACK_SIZE_TINY,
  1864. (LPTHREAD_START_ROUTINE)JoinDomainWorkThrd,
  1865. (LPVOID)pData, 0, &dwThreadId);
  1866. if (NULL != hthrd)
  1867. {
  1868. SetCursorToHourglass( hwndDlg, pData );
  1869. PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
  1870. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
  1871. CloseHandle(hthrd);
  1872. }
  1873. else
  1874. {
  1875. // Failed to create the required netsetup thread
  1876. AssertSz(0,"Unable to create JoinWorkgroupDomain thread.");
  1877. TraceHr(ttidWizard, FAL, E_OUTOFMEMORY, FALSE, "JoinWorkgroupDomain - Create thread failed");
  1878. }
  1879. }
  1880. //
  1881. // Function: OnJoinWizNext
  1882. //
  1883. // Purpose:
  1884. //
  1885. // Parameters:
  1886. //
  1887. // Returns:
  1888. //
  1889. BOOL OnJoinWizNext(HWND hwndDlg)
  1890. {
  1891. TraceFileFunc(ttidGuiModeSetup);
  1892. // Retrieve the CWizard instance from the dialog
  1893. CWizard * pWizard =
  1894. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  1895. Assert(NULL != pWizard);
  1896. JoinData * pData = reinterpret_cast<JoinData *>
  1897. (pWizard->GetPageData(IDD_Join));
  1898. Assert(NULL != pData);
  1899. if(!pData)
  1900. {
  1901. return false;
  1902. }
  1903. // Attempt to join the workgroup/domain if we have the Ident interface
  1904. //
  1905. if (pData->pIdent)
  1906. {
  1907. // Ensure the user supplied a workgroup/domain name
  1908. //
  1909. if (0 == GetWindowTextLength(GetDlgItem(hwndDlg, GetJoinNameIIDFromSelection(hwndDlg))))
  1910. {
  1911. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
  1912. return TRUE;
  1913. }
  1914. // Join the workgroup/domain
  1915. //
  1916. if (!IsUnattended(pWizard) || pData->fUnattendedFailed)
  1917. {
  1918. DNS_STATUS dnsStatus;
  1919. if ( IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN) )
  1920. {
  1921. dnsStatus = IsValidDomainName( hwndDlg );
  1922. }
  1923. else
  1924. {
  1925. dnsStatus = ERROR_SUCCESS;
  1926. }
  1927. if ( (dnsStatus == ERROR_SUCCESS) ||
  1928. (dnsStatus == DNS_ERROR_NON_RFC_NAME) )
  1929. {
  1930. JoinWorkgroupDomain(hwndDlg, pWizard, pData);
  1931. }
  1932. else
  1933. {
  1934. tstring str;
  1935. str = SzLoadIds(IDS_JOIN_E_DOMAIN_INVALID_NAME);
  1936. MessageBox(GetParent(hwndDlg), str.c_str(),
  1937. SzLoadIds(IDS_SETUP_CAPTION), MB_OK);
  1938. }
  1939. }
  1940. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
  1941. }
  1942. else
  1943. {
  1944. OnJoinSuccess(hwndDlg);
  1945. }
  1946. return TRUE;
  1947. }
  1948. //
  1949. // Function: dlgprocJoin
  1950. //
  1951. // Purpose: Dialog Procedure for the Join wizard page
  1952. //
  1953. // Parameters: standard dlgproc parameters
  1954. //
  1955. // Returns: INT_PTR
  1956. //
  1957. INT_PTR CALLBACK dlgprocJoin( HWND hwndDlg, UINT uMsg,
  1958. WPARAM wParam, LPARAM lParam )
  1959. {
  1960. TraceFileFunc(ttidGuiModeSetup);
  1961. BOOL frt = FALSE;
  1962. switch (uMsg)
  1963. {
  1964. case PWM_JOINFAILURE:
  1965. frt = OnJoinFailure(hwndDlg, lParam);
  1966. break;
  1967. case PWM_JOINSUCCESS:
  1968. frt = OnJoinSuccess(hwndDlg);
  1969. break;
  1970. case WM_INITDIALOG:
  1971. frt = OnJoinInitDialog(hwndDlg, lParam);
  1972. break;
  1973. case WM_COMMAND:
  1974. {
  1975. if ((BN_CLICKED == HIWORD(wParam)) &&
  1976. ((BTN_JOIN_DOMAIN == LOWORD(wParam)) ||
  1977. (BTN_JOIN_WORKGROUP == LOWORD(wParam))))
  1978. {
  1979. JoinUpdatePromptText(hwndDlg);
  1980. }
  1981. }
  1982. break;
  1983. case WM_NOTIFY:
  1984. {
  1985. LPNMHDR pnmh = (LPNMHDR)lParam;
  1986. switch (pnmh->code)
  1987. {
  1988. // propsheet notification
  1989. case PSN_HELP:
  1990. break;
  1991. case PSN_SETACTIVE:
  1992. frt = OnJoinPageActivate(hwndDlg);
  1993. break;
  1994. case PSN_APPLY:
  1995. break;
  1996. case PSN_KILLACTIVE:
  1997. break;
  1998. case PSN_RESET:
  1999. break;
  2000. case PSN_WIZBACK:
  2001. frt = OnJoinWizBack(hwndDlg);
  2002. break;
  2003. case PSN_WIZFINISH:
  2004. break;
  2005. case PSN_WIZNEXT:
  2006. frt = OnJoinWizNext(hwndDlg);
  2007. break;
  2008. default:
  2009. break;
  2010. }
  2011. }
  2012. break;
  2013. default:
  2014. break;
  2015. }
  2016. return( frt );
  2017. }
  2018. //
  2019. // Function: JoinPageCleanup
  2020. //
  2021. // Purpose: As a callback function to allow any page allocated memory
  2022. // to be cleaned up, after the page will no longer be accessed.
  2023. //
  2024. // Parameters: pWizard [IN] - The wizard against which the page called
  2025. // register page
  2026. // lParam [IN] - The lParam supplied in the RegisterPage call
  2027. //
  2028. // Returns: nothing
  2029. //
  2030. VOID JoinPageCleanup(CWizard *pWizard, LPARAM lParam)
  2031. {
  2032. TraceFileFunc(ttidGuiModeSetup);
  2033. JoinData * pData = reinterpret_cast<JoinData *>(lParam);
  2034. if (NULL != pData)
  2035. {
  2036. delete pData->pIdent;
  2037. }
  2038. MemFree(reinterpret_cast<void*>(lParam));
  2039. }
  2040. //
  2041. // Function: CreateJoinPage
  2042. //
  2043. // Purpose: To determine if the Join page needs to be shown, and to
  2044. // to create the page if requested. Note the Join page is
  2045. // responsible for initial installs also.
  2046. //
  2047. // Parameters: pWizard [IN] - Ptr to a Wizard instance
  2048. // pData [IN] - Context data to describe the world in
  2049. // which the Wizard will be run
  2050. // fCountOnly [IN] - If True, only the maximum number of
  2051. // pages this routine will create need
  2052. // be determined.
  2053. // pnPages [IN] - Increment by the number of pages
  2054. // to create/created
  2055. //
  2056. // Returns: HRESULT, S_OK on success
  2057. //
  2058. HRESULT HrCreateJoinPage(CWizard *pWizard, PINTERNAL_SETUP_DATA pData,
  2059. BOOL fCountOnly, UINT *pnPages)
  2060. {
  2061. TraceFileFunc(ttidGuiModeSetup);
  2062. HRESULT hr = S_OK;
  2063. // Batch Mode or for fresh install
  2064. if (!IsPostInstall(pWizard))
  2065. {
  2066. // If not only counting, create and register the page
  2067. if (!fCountOnly)
  2068. {
  2069. JoinData * pData = NULL;
  2070. HPROPSHEETPAGE hpsp;
  2071. PROPSHEETPAGE psp;
  2072. TraceTag(ttidWizard, "Creating Join Page");
  2073. hr = E_OUTOFMEMORY;
  2074. pData = reinterpret_cast<JoinData *>(MemAlloc(sizeof(JoinData)));
  2075. if (pData)
  2076. {
  2077. pData->fUnattendedFailed = FALSE;
  2078. pData->fUpgraded = FALSE;
  2079. pData->pIdent = NULL;
  2080. pData->hOldCursor = NULL;
  2081. pData->hwndDlg = NULL;
  2082. pData->dwJoinFlag = 0;
  2083. pData->szUserName[0] = 0;
  2084. pData->szPassword[0] = 0;
  2085. pData->szDomain[0] = 0;
  2086. pData->pszMachineObjectOU = NULL;
  2087. pData->szComputerPassword[0] = 0;
  2088. psp.dwSize = sizeof( PROPSHEETPAGE );
  2089. psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
  2090. psp.hInstance = _Module.GetResourceInstance();
  2091. psp.pszTemplate = MAKEINTRESOURCE( IDD_Join );
  2092. psp.hIcon = NULL;
  2093. psp.pfnDlgProc = dlgprocJoin;
  2094. psp.lParam = reinterpret_cast<LPARAM>(pWizard);
  2095. psp.pszHeaderTitle = SzLoadIds(IDS_T_Join);
  2096. psp.pszHeaderSubTitle = SzLoadIds(IDS_ST_Join);
  2097. hpsp = CreatePropertySheetPage( &psp );
  2098. if (hpsp)
  2099. {
  2100. pWizard->RegisterPage(IDD_Join, hpsp,
  2101. JoinPageCleanup,
  2102. reinterpret_cast<LPARAM>(pData));
  2103. hr = S_OK;
  2104. }
  2105. else
  2106. {
  2107. MemFree(pData);
  2108. }
  2109. }
  2110. }
  2111. if (SUCCEEDED(hr))
  2112. {
  2113. (*pnPages)++;
  2114. }
  2115. }
  2116. TraceHr(ttidWizard, FAL, hr, FALSE, "HrCreateJoinPage");
  2117. return hr;
  2118. }
  2119. //
  2120. // Function: AppendJoinPage
  2121. //
  2122. // Purpose: Add the Join page, if it was created, to the set of pages
  2123. // that will be displayed.
  2124. //
  2125. // Parameters: pWizard [IN] - Ptr to Wizard Instance
  2126. // pahpsp [IN,OUT] - Array of pages to add our page to
  2127. // pcPages [IN,OUT] - Count of pages in pahpsp
  2128. //
  2129. // Returns: Nothing
  2130. //
  2131. VOID AppendJoinPage(CWizard *pWizard, HPROPSHEETPAGE* pahpsp, UINT *pcPages)
  2132. {
  2133. TraceFileFunc(ttidGuiModeSetup);
  2134. if (!IsPostInstall(pWizard))
  2135. {
  2136. HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_Join);
  2137. Assert(hPage);
  2138. pahpsp[*pcPages] = hPage;
  2139. (*pcPages)++;
  2140. }
  2141. }