Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2510 lines
77 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. EXTERN_C const INT MAX_DOMAINNAME_LENGTH = DNS_MAX_NAME_LENGTH - SRV_RECORD_RESERVE;
  42. EXTERN_C 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. LRESULT 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 (HRESULT)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 (HRESULT)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 (HRESULT)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( LogSevWarning,
  691. SzLoadIds (IDS_E_UNATTENDED_JOIN_DEFAULT_WORKGROUP),
  692. hr );
  693. }
  694. else
  695. {
  696. MessageBox(GetParent(hwndDlg), SzFromError(hr, TRUE),
  697. SzLoadIds(IDS_SETUP_CAPTION), MB_OK);
  698. }
  699. goto Done;
  700. }
  701. }
  702. else
  703. {
  704. AssertSz(0,"Cannot find the INegCfgIdentification interface!");
  705. }
  706. Done:
  707. delete pINetid;
  708. TraceHr(ttidWizard, FAL, hr, FALSE, "JoinDefaultWorkgroup");
  709. }
  710. BOOL OnJoinSuccess(HWND hwndDlg)
  711. {
  712. TraceFileFunc(ttidGuiModeSetup);
  713. CWizard * pWizard =
  714. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  715. Assert(NULL != pWizard);
  716. if ( !pWizard ) {
  717. return TRUE;
  718. }
  719. // Reset the Wait Cursor
  720. JoinData * pData = reinterpret_cast<JoinData *>
  721. (pWizard->GetPageData(IDD_Join));
  722. Assert(NULL != pData);
  723. if ( !pData ) {
  724. return TRUE;
  725. }
  726. RestoreCursor( hwndDlg, pData );
  727. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, TRUE);
  728. EnableAndDisableWorkGroupDomainControls(hwndDlg);
  729. if (!(IsUnattended(pWizard) && pData->fUpgraded))
  730. {
  731. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
  732. }
  733. // Goto the Exit page
  734. pWizard->SetPageDirection(IDD_Join, NWPD_BACKWARD);
  735. HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_Exit);
  736. PostMessage(GetParent(hwndDlg), PSM_SETCURSEL, 0,
  737. (LPARAM)(HPROPSHEETPAGE)hPage);
  738. return TRUE;
  739. }
  740. BOOL OnJoinFailure(HWND hwndDlg, LPARAM lParam)
  741. {
  742. TraceFileFunc(ttidGuiModeSetup);
  743. JoinData * pData = NULL;
  744. BOOL fWorkgroup;
  745. tstring str;
  746. HRESULT hr = (HRESULT)lParam;
  747. CWizard * pWizard =
  748. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  749. Assert(NULL != pWizard);
  750. if (pWizard)
  751. {
  752. pData = reinterpret_cast<JoinData *>(pWizard->GetPageData(IDD_Join));
  753. Assert(NULL != pData);
  754. }
  755. fWorkgroup = !IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN);
  756. if (fWorkgroup)
  757. {
  758. str = SzLoadIds(IDS_JOIN_E_WORKGROUP_MSG);
  759. }
  760. else
  761. {
  762. if (pData && (pData->dwJoinFlag & JDF_WIN9x_UPGRADE))
  763. {
  764. str = SzLoadIds(IDS_JOIN_E_DOMAIN_WIN9X_MSG_1);
  765. str += SzLoadIds(IDS_JOIN_E_DOMAIN_WIN9X_MSG_2);
  766. }
  767. else
  768. {
  769. // Raid 372087: removed additional text
  770. str = SzLoadIds(IDS_JOIN_E_DOMAIN_MSG);
  771. }
  772. }
  773. // If an error actually occurred, ask the user if they want to ignore
  774. // the error and proceed. Note Unattended can succeed but require us
  775. // to stay on the page so this function is envoked to do this.
  776. if (FAILED(hr))
  777. {
  778. if (IDYES == NcMsgBox(GetParent(hwndDlg),
  779. IDS_SETUP_CAPTION, IDS_JOIN_FAILURE,
  780. MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2,
  781. SzFromError(hr, fWorkgroup), str.c_str()))
  782. {
  783. // User choose to proceed in spite of the failure, go to the Exit page
  784. if (!fWorkgroup)
  785. {
  786. NotifyPostSetupWizard(PSW_JOINFAILED, pWizard);
  787. }
  788. OnJoinSuccess(hwndDlg);
  789. return TRUE;
  790. }
  791. }
  792. if (pData)
  793. {
  794. // Reset the Wait Cursor
  795. RestoreCursor( hwndDlg, pData );
  796. }
  797. // Make sure the page is visible.
  798. if (g_pSetupWizard != NULL)
  799. {
  800. g_pSetupWizard->PSetupData()->ShowHideWizardPage(TRUE);
  801. }
  802. // The user wants to stay and correct whatever's wrong
  803. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, TRUE);
  804. EnableAndDisableWorkGroupDomainControls(hwndDlg);
  805. UpdateNextBackBttns(hwndDlg);
  806. return TRUE;
  807. }
  808. // ONLY return failure if the workstation is installed and it doesn't start for 2 minutes
  809. HRESULT HrWorkstationStart(HWND hwndDlg)
  810. {
  811. TraceFileFunc(ttidGuiModeSetup);
  812. HRESULT hr = S_OK;
  813. HRESULT hrTmp;
  814. CServiceManager scm;
  815. TraceTag(ttidWizard, "Entering HrWorkstationStart...Checking for LanmanWorkstation presence");
  816. // Open the service control manager
  817. //
  818. hrTmp = scm.HrOpen();
  819. if (SUCCEEDED(hrTmp))
  820. {
  821. // Find the workstation service
  822. //
  823. SC_HANDLE hSvc = OpenService (scm.Handle(),
  824. c_szSvcWorkstation,
  825. SERVICE_QUERY_CONFIG |
  826. SERVICE_QUERY_STATUS |
  827. SERVICE_ENUMERATE_DEPENDENTS |
  828. SERVICE_START | SERVICE_STOP |
  829. SERVICE_USER_DEFINED_CONTROL);
  830. if (hSvc)
  831. {
  832. SERVICE_STATUS status;
  833. const UINT cmsWait = 100;
  834. const UINT cmsTotal = 120000; // 2 minutes
  835. UINT cLoop = cmsTotal / cmsWait;
  836. // Check it's status, exit test once it's running
  837. //
  838. for (UINT nLoop = 0; nLoop < cLoop; nLoop++, Sleep (cmsWait))
  839. {
  840. BOOL fr = QueryServiceStatus (hSvc, &status);
  841. Assert(fr);
  842. if (SERVICE_RUNNING == status.dwCurrentState)
  843. {
  844. break;
  845. }
  846. }
  847. if (SERVICE_RUNNING != status.dwCurrentState)
  848. {
  849. hr = HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE);
  850. #if DBG
  851. OutputDebugString (L"***ERROR*** NETCFG - Workstation service didn't start after more than 2 minutes!\n");
  852. OutputDebugString (L"***ERROR*** NETCFG - Join Domain will fail!\n");
  853. #endif
  854. }
  855. CloseServiceHandle(hSvc);
  856. }
  857. scm.Close();
  858. }
  859. else
  860. {
  861. TraceError("WJOIN.CPP - HrWorkstationStart - Unable to open the Service Manager",hrTmp);
  862. }
  863. TraceError("WJOIN.CPP - HrWorkstationStart",hr);
  864. TraceTag(ttidWizard, "Leaving HrWorkstationStart");
  865. return hr;
  866. }
  867. // Purpose: Secure Domain Join with the new ComputerPassword Answer-File key.
  868. // Domain Join tries to join the domain with the random precreated machine password.
  869. // (the username is not required in this case)
  870. // note: code path is inspired from HrAttemptJoin(JoinData * pData)
  871. // functions defined in ncident.cpp
  872. extern HRESULT HrNetValidateName(IN PCWSTR lpMachine,
  873. IN PCWSTR lpName,
  874. IN PCWSTR lpAccount,
  875. IN PCWSTR lpPassword,
  876. IN NETSETUP_NAME_TYPE NameType);
  877. extern HRESULT HrNetJoinDomain(IN PWSTR lpMachine,
  878. IN PWSTR lpMachineObjectOU,
  879. IN PWSTR lpDomain,
  880. IN PWSTR lpAccount,
  881. IN PWSTR lpPassword,
  882. IN DWORD fJoinOptions);
  883. EXTERN_C HRESULT HrAttemptSecureDomainJoin(JoinData * pData)
  884. {
  885. TraceFileFunc(ttidGuiModeSetup);
  886. HRESULT hr;
  887. Assert(pData);
  888. // 1. wait for the start of LanmanWorkstation service
  889. // 2. check for valid domain
  890. // 3. secure join domain
  891. hr = HrWorkstationStart(pData->hwndDlg);
  892. if (SUCCEEDED(hr))
  893. {
  894. hr = HrNetValidateName(NULL, pData->szDomain , NULL, NULL, NetSetupDomain);
  895. }
  896. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptSecureDomainJoin - HrNetValidateName");
  897. if (SUCCEEDED(hr))
  898. {
  899. // do the secure join domain
  900. DWORD dwJoinOption = 0;
  901. dwJoinOption |= (NETSETUP_JOIN_DOMAIN | NETSETUP_JOIN_UNSECURE | NETSETUP_MACHINE_PWD_PASSED);
  902. if (FInSystemSetup())
  903. {
  904. // During system setup, need to pass special flag that tells join code
  905. // to not do certain operations because SAM is not initialized yet.
  906. dwJoinOption |= NETSETUP_INSTALL_INVOCATION;
  907. }
  908. hr = HrNetJoinDomain(NULL,pData->pszMachineObjectOU,
  909. pData->szDomain, NULL, pData->szComputerPassword,
  910. dwJoinOption);
  911. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptSecureDomainJoin - HrNetJoinDomain");
  912. }
  913. TraceError("HrAttemptSecureDomainJoin", hr);
  914. return hr;
  915. }
  916. EXTERN_C HRESULT HrAttemptJoin(JoinData * pData)
  917. {
  918. TraceFileFunc(ttidGuiModeSetup);
  919. HRESULT hr;
  920. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  921. {
  922. hr = HrWorkstationStart(pData->hwndDlg);
  923. if (SUCCEEDED(hr))
  924. {
  925. hr = pData->pIdent->JoinDomain(pData->szDomain,
  926. pData->pszMachineObjectOU,
  927. pData->szUserName,
  928. pData->szPassword, pData->dwJoinFlag);
  929. }
  930. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - JoinDomain");
  931. }
  932. else
  933. {
  934. // Join a workgroup
  935. hr = pData->pIdent->JoinWorkgroup(pData->szDomain);
  936. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - JoinWorkgroup");
  937. }
  938. if (SUCCEEDED(hr))
  939. {
  940. if (S_OK == pData->pIdent->Validate())
  941. {
  942. hr = pData->pIdent->Apply();
  943. TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - Apply");
  944. }
  945. }
  946. if (FAILED(hr))
  947. {
  948. // Rollback any changes
  949. pData->pIdent->Cancel();
  950. }
  951. TraceError("HrAttemptJoin",hr);
  952. return hr;
  953. }
  954. HRESULT HrAttemptJoin(JoinData * pData, DWORD dwRetries, DWORD dwDelayPeriod)
  955. {
  956. HRESULT hr = E_FAIL;
  957. DWORD dwCount = dwRetries;
  958. do
  959. {
  960. hr = HrAttemptJoin(pData);
  961. if (FAILED(hr))
  962. {
  963. dwCount--;
  964. Sleep(dwDelayPeriod);
  965. }
  966. } while (FAILED(hr) && (dwCount));
  967. return hr;
  968. }
  969. HRESULT HrAttemptSecureDomainJoin(JoinData * pData, DWORD dwRetries, DWORD dwDelayPeriod)
  970. {
  971. HRESULT hr = E_FAIL;
  972. DWORD dwCount = dwRetries;
  973. do
  974. {
  975. hr = HrAttemptSecureDomainJoin(pData);
  976. if (FAILED(hr))
  977. {
  978. dwCount--;
  979. Sleep(dwDelayPeriod);
  980. }
  981. } while (FAILED(hr) && (dwCount));
  982. return hr;
  983. }
  984. EXTERN_C DWORD JoinDomainWorkThrd(JoinData * pData)
  985. {
  986. TraceFileFunc(ttidGuiModeSetup);
  987. BOOL fUninitCOM = FALSE;
  988. HRESULT hr;
  989. Assert(NULL != pData);
  990. CWizard * pWizard =
  991. reinterpret_cast<CWizard *>(::GetWindowLongPtr(pData->hwndDlg, DWLP_USER));
  992. Assert(NULL != pWizard);
  993. // Initialize COM on this thread
  994. //
  995. hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
  996. if (FAILED(hr))
  997. {
  998. // $REVIEW - LogError ?
  999. TraceTag(ttidWizard, "Failed to initialize COM join work thread");
  1000. goto Done;
  1001. }
  1002. else
  1003. {
  1004. // Remember to uninitialize COM on thread exit
  1005. fUninitCOM = TRUE;
  1006. }
  1007. DWORD dwNumTries = 1;
  1008. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  1009. {
  1010. Sleep(c_dwDomainJoinWaitDelay);
  1011. dwNumTries = 5;
  1012. }
  1013. if (pData->dwJoinFlag & JDF_MACHINE_PWD_PASSED)
  1014. { // Unattended Answer-File specified with ComputerPassword key
  1015. // Try Secure Domain Join
  1016. TraceTag(ttidWizard, "Attempting join with precreated computer password.");
  1017. hr = HrAttemptSecureDomainJoin(pData, dwNumTries, c_dwDomainJoinWaitDelay);
  1018. if (FAILED(hr))
  1019. {
  1020. // clear the secure domain join flag and try the normal join
  1021. TraceTag(ttidWizard, "Failed in secure join domain.");
  1022. pData->dwJoinFlag &= ~JDF_MACHINE_PWD_PASSED;
  1023. }
  1024. else
  1025. goto Cleanup;
  1026. }
  1027. // Try the normal join
  1028. //
  1029. TraceTag(ttidWizard, "Attempting join WITHOUT trying to create an account.");
  1030. hr = HrAttemptJoin(pData, 3, 10000);
  1031. // If the join failed, and the Create Account flag has not been
  1032. // specified, try adding it and reattempt the join.
  1033. //
  1034. if (FAILED(hr) && !(pData->dwJoinFlag & JDF_CREATE_ACCOUNT))
  1035. {
  1036. // Clear the Unsecure join flag if set, creating an account is
  1037. // mutually exclusive. Set the create account flag to reattempt.
  1038. //
  1039. pData->dwJoinFlag &= ~JDF_JOIN_UNSECURE;
  1040. pData->dwJoinFlag |= JDF_CREATE_ACCOUNT;
  1041. TraceTag(ttidWizard, "Attempting join but trying to create an account.");
  1042. hr = HrAttemptJoin(pData, dwNumTries, c_dwDomainJoinWaitDelay);
  1043. }
  1044. Cleanup:
  1045. // Cleanup username/password and MachineObjectOU
  1046. //
  1047. pData->szUserName[0] = 0;
  1048. pData->szPassword[0] = 0;
  1049. pData->dwJoinFlag = 0;
  1050. MemFree(pData->pszMachineObjectOU);
  1051. pData->pszMachineObjectOU = NULL;
  1052. pData->szComputerPassword[0] = 0;
  1053. if (FAILED(hr))
  1054. {
  1055. // Raid 380374: no UI allowed if in full unattended mode ..
  1056. if (UM_FULLUNATTENDED == pWizard->GetUnattendedMode())
  1057. {
  1058. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  1059. {
  1060. NetSetupLogStatusV( LogSevError,
  1061. SzLoadIds (IDS_E_UNATTENDED_JOIN_DOMAIN),
  1062. pData->szDomain,
  1063. hr );
  1064. }
  1065. else
  1066. {
  1067. NetSetupLogStatusV( LogSevWarning,
  1068. SzLoadIds (IDS_E_UNATTENDED_JOIN_WORKGROUP),
  1069. pData->szDomain,
  1070. hr );
  1071. }
  1072. // proceed to the exit page
  1073. PostMessage(pData->hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1074. }
  1075. else
  1076. {
  1077. // If we're in unattended mode, consider it failed.
  1078. pData->fUnattendedFailed = TRUE;
  1079. PostMessage(pData->hwndDlg, PWM_JOINFAILURE, 0, (LPARAM)hr);
  1080. }
  1081. }
  1082. else
  1083. {
  1084. if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN))
  1085. NotifyPostSetupWizard(PSW_JOINEDDOMAIN, pWizard);
  1086. PostMessage(pData->hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1087. }
  1088. Done:
  1089. // Uninitialize COM for this thread
  1090. //
  1091. if (fUninitCOM)
  1092. {
  1093. CoUninitialize();
  1094. }
  1095. TraceTag(ttidWizard, "Leaving JoinDomainWorkThrd...");
  1096. return( 0 );
  1097. }
  1098. //
  1099. // Function: HrJoinProcessAnswerFile
  1100. //
  1101. // Purpose: Read the answer file and populate the in memory structures
  1102. // and UI with the data found
  1103. //
  1104. // Parameters:
  1105. //
  1106. // Returns: HRESULT, S_OK on success
  1107. // S_FALSE if required information is missing
  1108. // A failed error code on error
  1109. //
  1110. HRESULT HrJoinProcessAnswerFile(HWND hwndDlg, CWizard * pWizard,
  1111. JoinData * pData)
  1112. {
  1113. TraceFileFunc(ttidGuiModeSetup);
  1114. CSetupInfFile csif;
  1115. INFCONTEXT ctx;
  1116. BOOL fValue;
  1117. HRESULT hr;
  1118. int nId = BTN_JOIN_WORKGROUP;
  1119. tstring str;
  1120. pData->dwJoinFlag = 0;
  1121. pData->szDomain[0] = 0;
  1122. pData->szUserName[0] = 0;
  1123. pData->szPassword[0] = 0;
  1124. pData->pszMachineObjectOU = NULL;
  1125. pData->szComputerPassword[0] = 0;
  1126. if ((NULL == pWizard->PSetupData()) ||
  1127. (NULL == pWizard->PSetupData()->UnattendFile))
  1128. {
  1129. hr = NETSETUP_E_ANS_FILE_ERROR;
  1130. goto Error;
  1131. }
  1132. // Open the answerfile
  1133. //
  1134. hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile,
  1135. NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
  1136. if (FAILED(hr))
  1137. {
  1138. hr = S_FALSE;
  1139. TraceTag(ttidWizard, "Unable to open answer file!!!");
  1140. goto Error;
  1141. }
  1142. // Check for existance of the identification section. If it is not
  1143. // present return S_FALSE to indicate identification info not supplied
  1144. //
  1145. hr = HrSetupFindFirstLine (csif.Hinf(), c_szAfSectionIdentification,
  1146. NULL, &ctx);
  1147. if (SPAPI_E_LINE_NOT_FOUND == hr)
  1148. {
  1149. hr = S_FALSE;
  1150. goto Error;
  1151. }
  1152. // Try to get the workgroup
  1153. //
  1154. hr = csif.HrGetString(c_szAfSectionIdentification,
  1155. c_szAfJoinWorkgroup, &str);
  1156. if (SUCCEEDED(hr) && str.length())
  1157. {
  1158. if (MAX_WORKGROUPNAME_LENGTH >= str.length())
  1159. {
  1160. wcscpy(pData->szDomain, str.c_str());
  1161. TraceTag(ttidWizard, "Joining workgroup: %S", pData->szDomain);
  1162. }
  1163. else
  1164. {
  1165. hr = NETSETUP_E_ANS_FILE_ERROR;
  1166. TraceTag(ttidWizard, "JOIN Workgroup - Invalid workgroup supplied.");
  1167. goto Error;
  1168. }
  1169. }
  1170. else
  1171. {
  1172. if ( IsRunningOnPersonal() )
  1173. {
  1174. // No workgroup entry, skip joining since we are already
  1175. // in the default workgroup.
  1176. hr = S_FALSE;
  1177. goto Error;
  1178. }
  1179. else
  1180. {
  1181. // Try to get the domain
  1182. //
  1183. hr = csif.HrGetString(c_szAfSectionIdentification,
  1184. c_szAfJoinDomain, &str);
  1185. if (SPAPI_E_LINE_NOT_FOUND == hr)
  1186. {
  1187. // No domain or workgroup entry, skip joining a domain
  1188. hr = S_FALSE;
  1189. goto Error;
  1190. }
  1191. else if (FAILED(hr) || (0 == str.length()) ||
  1192. (MAX_DOMAINNAME_LENGTH < str.length()))
  1193. {
  1194. hr = NETSETUP_E_ANS_FILE_ERROR;
  1195. TraceTag(ttidWizard, "JOIN Domain - Invalid domain supplied.");
  1196. goto Error;
  1197. }
  1198. // Joining a domain...
  1199. //
  1200. nId = BTN_JOIN_DOMAIN;
  1201. wcscpy(pData->szDomain, str.c_str());
  1202. TraceTag(ttidWizard, "Joining domain: %S", pData->szDomain);
  1203. // If we're upgrading from win9x add the special flag
  1204. //
  1205. hr = csif.HrGetString(c_szAfSectionNetworking,
  1206. c_szAfUpgradeFromProduct, &str);
  1207. if (SUCCEEDED(hr) &&
  1208. (0 == lstrcmpiW(str.c_str(), c_szAfWin95)))
  1209. {
  1210. pData->dwJoinFlag |= JDF_WIN9x_UPGRADE;
  1211. }
  1212. // Support unsecure joins
  1213. //
  1214. hr = csif.HrGetStringAsBool(c_szAfSectionIdentification,
  1215. c_szAfUnsecureJoin,
  1216. &fValue);
  1217. if (SUCCEEDED(hr) && fValue)
  1218. {
  1219. pData->dwJoinFlag |= JDF_JOIN_UNSECURE;
  1220. }
  1221. // Is a MachineObjectOU specified?
  1222. //
  1223. hr = csif.HrGetString(c_szAfSectionIdentification,
  1224. c_szAfMachineObjectOU, &str);
  1225. if (SUCCEEDED(hr) && str.length())
  1226. {
  1227. pData->pszMachineObjectOU = reinterpret_cast<WCHAR *>(MemAlloc(sizeof(WCHAR) * (str.length() + 1)));
  1228. if (pData->pszMachineObjectOU)
  1229. {
  1230. TraceTag(ttidWizard, "Machine Object OU: %S", pData->szDomain);
  1231. lstrcpyW(pData->pszMachineObjectOU, str.c_str());
  1232. }
  1233. }
  1234. // Bug# 204377 secure domain join shouldn't require both "ComputerPassword" and
  1235. // "DomainAdmin"/"DomainAdminPassword" options to be presence
  1236. // in Answer-File simultaneously.
  1237. //
  1238. // Bug# 204378 If the Answer-File specifies the "ComputerPassword" key
  1239. // in the "Indentification" section, the code should attempt
  1240. // a secure domain join, regardless of the presence of the
  1241. // "DoOldstyleDomainjoin" key
  1242. // check if this is a secure domain join by
  1243. // quering the random machine account password
  1244. hr = csif.HrGetString(c_szAfSectionIdentification,
  1245. c_szAfComputerPassword, &str);
  1246. if (SUCCEEDED(hr) && (str.length() <= SAM_MAX_PASSWORD_LENGTH) && str.length())
  1247. {
  1248. TraceTag(ttidWizard, "Got the value of ComputerPassword");
  1249. wcscpy(pData->szComputerPassword, str.c_str());
  1250. // signal that we need to try secure domain join by
  1251. // passing the random machine password
  1252. pData->dwJoinFlag |= JDF_MACHINE_PWD_PASSED;
  1253. // Bug# 204378, make sure we won't do the unsecure domain join
  1254. // and we'll try to read "DomainAdmin"/"DomainAdminPassword"
  1255. // in the following if block.
  1256. pData->dwJoinFlag &= ~JDF_JOIN_UNSECURE;
  1257. }
  1258. // If not a remote boot client, Query the username, unless this
  1259. // is an unsecure domain join which doesn't need username/password.
  1260. //
  1261. if (
  1262. #if defined(REMOTE_BOOT)
  1263. (S_FALSE == HrIsRemoteBootMachine()) ||
  1264. #endif // defined(REMOTE_BOOT)
  1265. ((pData->dwJoinFlag & JDF_JOIN_UNSECURE) == 0))
  1266. {
  1267. hr = csif.HrGetString(c_szAfSectionIdentification,
  1268. c_szAfDomainAdmin, &str);
  1269. if (SUCCEEDED(hr) && (MAX_USERNAME_LENGTH > str.length()))
  1270. {
  1271. wcscpy(pData->szUserName, str.c_str());
  1272. // Query the password
  1273. //
  1274. hr = csif.HrGetString(c_szAfSectionIdentification,
  1275. c_szAfDomainAdminPassword, &str);
  1276. if (SUCCEEDED(hr) && (SAM_MAX_PASSWORD_LENGTH > str.length()))
  1277. {
  1278. wcscpy(pData->szPassword, str.c_str());
  1279. // Raid 195920 - If both username and password are
  1280. // present, treat this like a fresh install and DO NOT
  1281. // use the JDF_WIN9x_UPGRADE flag
  1282. pData->dwJoinFlag &= ~(JDF_WIN9x_UPGRADE | JDF_JOIN_UNSECURE);
  1283. }
  1284. }
  1285. // Bug# 204377
  1286. // ignore any error on reading of "DomainAdmin"/"DomainAdminPassword" keys
  1287. // if "ComputerPassword" has been specified.
  1288. if (! (pData->dwJoinFlag & JDF_MACHINE_PWD_PASSED) )
  1289. {
  1290. // If failed or either is longer than the maximum length return an error
  1291. //
  1292. if (FAILED(hr) || (MAX_USERNAME_LENGTH <= str.length()))
  1293. {
  1294. hr = NETSETUP_E_ANS_FILE_ERROR;
  1295. TraceTag(ttidWizard, "JOIN Domain - Invalid username/password supplied.");
  1296. goto Error;
  1297. }
  1298. }
  1299. }
  1300. }
  1301. }
  1302. // Normalize any optional errors
  1303. //
  1304. hr = S_OK;
  1305. Error:
  1306. // Update the UI and pData with the info we managed to read.
  1307. //
  1308. CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, nId);
  1309. SetWindowText(GetDlgItem(hwndDlg, nId == BTN_JOIN_DOMAIN ? EDT_DOMAINJOIN_NAME : EDT_WORKGROUPJOIN_NAME ), pData->szDomain);
  1310. JoinUpdatePromptText(hwndDlg);
  1311. TraceHr(ttidWizard, FAL, hr, FALSE, "HrJoinProcessAnswerFile");
  1312. return hr;
  1313. }
  1314. //
  1315. // Function: OnJoinDoUnattended
  1316. //
  1317. // Purpose:
  1318. //
  1319. // Parameters:
  1320. //
  1321. // Returns: BOOL
  1322. //
  1323. BOOL OnJoinDoUnattended(HWND hwndDlg)
  1324. {
  1325. TraceFileFunc(ttidGuiModeSetup);
  1326. DWORD dwThreadId = 0;
  1327. HRESULT hr = S_OK;
  1328. CWizard * pWizard =
  1329. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  1330. Assert(NULL != pWizard);
  1331. JoinData * pData = reinterpret_cast<JoinData *>
  1332. (pWizard->GetPageData(IDD_Join));
  1333. Assert(NULL != pData);
  1334. Assert(NULL != pData->pIdent);
  1335. Assert(NULL != pData->hwndDlg);
  1336. if(pData) {
  1337. // Create the unattended thread
  1338. //
  1339. HANDLE hthrd = CreateThread(NULL, STACK_SIZE_TINY,
  1340. (LPTHREAD_START_ROUTINE)JoinDomainWorkThrd,
  1341. (LPVOID)pData, 0, &dwThreadId);
  1342. if (NULL != hthrd)
  1343. {
  1344. // Set the wait cursor
  1345. //
  1346. SetCursorToHourglass( hwndDlg, pData );
  1347. // Disable all the controls
  1348. //
  1349. PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
  1350. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
  1351. CloseHandle(hthrd);
  1352. }
  1353. else
  1354. {
  1355. hr = HrFromLastWin32Error();
  1356. }
  1357. }
  1358. TraceHr(ttidWizard, FAL, hr, FALSE, "OnJoinDoUnattended");
  1359. return (SUCCEEDED(hr));
  1360. }
  1361. // Determines whether the computer DNS domain name should be kept in sync with
  1362. // the DNS domain name of the domain to which it is joined. If no
  1363. // synchonization is required (unlikely, as sync is the default), do nothing.
  1364. //
  1365. // Otherwise, attempt to determine the DNS domain name of the domain and write
  1366. // this value into reg key. If the DNS domain name cannot be determined,
  1367. // write a flag to a separate reg key to indicate that whatever services that
  1368. // care about the computer DNS domain name (i.e. kerberos authentication)
  1369. // should try to fix up the name.
  1370. void
  1371. fixupComputerDNSDomainName()
  1372. {
  1373. TraceFileFunc(ttidGuiModeSetup);
  1374. TraceTag(ttidWizard, "Entering fixupComputerDNSDomainName");
  1375. // check a reg key for the sync flag.
  1376. bool fSetName = false;
  1377. HKEY hkeyParams = 0;
  1378. HRESULT hr =
  1379. HrRegOpenKeyEx(
  1380. HKEY_LOCAL_MACHINE,
  1381. c_szIpParameters,
  1382. // all access as we may need to write a value here if we fail.
  1383. KEY_READ_WRITE,
  1384. &hkeyParams);
  1385. if (SUCCEEDED(hr))
  1386. {
  1387. DWORD dwValue = 0;
  1388. hr = HrRegQueryDword(hkeyParams, c_szSyncDomainWithMembership, &dwValue);
  1389. if (SUCCEEDED(hr) && (1 == dwValue))
  1390. {
  1391. // the sync flag was present in the registry, and its value is true
  1392. fSetName = true;
  1393. }
  1394. else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
  1395. {
  1396. // the sync flag was not present in the registry, which means we
  1397. // assume the default value of true.
  1398. fSetName = true;
  1399. hr = S_OK;
  1400. }
  1401. }
  1402. if (SUCCEEDED(hr) && fSetName)
  1403. {
  1404. bool fixup_success = false;
  1405. // 293301 Here we attempt to determine the DNS domain name of the domain
  1406. // the machine is joined to. If we cannot determine that name (because
  1407. // a dc could not be located, for example), then we will set a flag in
  1408. // the registry so that kerberos authentication agents will come along
  1409. // and fixup the DNS domain name.
  1410. PDOMAIN_CONTROLLER_INFO pDCInfo = 0;
  1411. DWORD dw =
  1412. DsGetDcName(
  1413. 0,
  1414. 0,
  1415. 0,
  1416. 0,
  1417. // make sure to ask for the DNS domain name, otherwise we're
  1418. // likely to get the flatname
  1419. DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME,
  1420. &pDCInfo);
  1421. if (NOERROR == dw)
  1422. {
  1423. Assert(pDCInfo->DomainName);
  1424. Assert(pDCInfo->Flags & DS_DNS_DOMAIN_FLAG);
  1425. TraceTag(ttidWizard, "DsGetDcName succeeded %s", pDCInfo->DomainName);
  1426. if (pDCInfo->Flags & DS_DNS_DOMAIN_FLAG)
  1427. {
  1428. // the domain name is indeed the DNS name
  1429. // chop off any trailing '.'
  1430. WCHAR* AbsoluteSignifier =
  1431. &pDCInfo->DomainName[ wcslen(pDCInfo->DomainName) - 1 ];
  1432. if (*AbsoluteSignifier == L'.')
  1433. {
  1434. *AbsoluteSignifier = 0;
  1435. }
  1436. // set the computer's DNS domain name.
  1437. if (
  1438. SetComputerNameEx(
  1439. ComputerNamePhysicalDnsDomain,
  1440. pDCInfo->DomainName) )
  1441. {
  1442. fixup_success = true;
  1443. }
  1444. #if DBG
  1445. else
  1446. {
  1447. // this just isn't our day.
  1448. TraceTag(ttidWizard, "SetComputerNameEx failed");
  1449. }
  1450. #endif
  1451. }
  1452. NetApiBufferFree(pDCInfo);
  1453. }
  1454. #if DBG
  1455. else
  1456. {
  1457. TraceTag(ttidWizard, "DsGetDcName returned %ld",dw);
  1458. }
  1459. #endif
  1460. // at this point, fixup_success will indicate whether we successfully
  1461. // set the computer's domain DNS name, or not. If not, then we need
  1462. // to write a flag into the registry so that kerberos auth will fix
  1463. // it up later.
  1464. if (!fixup_success)
  1465. {
  1466. // write a flag to have someone else do the fixup
  1467. HrRegSetDword(hkeyParams, L"DoDNSDomainFixup", 1);
  1468. }
  1469. }
  1470. RegCloseKey(hkeyParams);
  1471. }
  1472. //
  1473. // Function: JoinUpgradeNT351orNT4toNT5
  1474. //
  1475. // Purpose: If currently processing NT4 -> NT5 upgrade, set the
  1476. // computer name.
  1477. //
  1478. // Parameters:
  1479. //
  1480. // Returns: none
  1481. //
  1482. VOID JoinUpgradeNT351orNT4toNT5(CWizard * pWizard, JoinData * pData)
  1483. {
  1484. TraceFileFunc(ttidGuiModeSetup);
  1485. HRESULT hr = S_OK;
  1486. CSetupInfFile csif;
  1487. INFCONTEXT ctx;
  1488. TraceTag(ttidWizard, "Checking for the need to do NT4->NT5 Join conversions...");
  1489. // If unattended
  1490. //
  1491. if (IsUnattended(pWizard) && (NULL != pWizard->PSetupData()) &&
  1492. (NULL != pWizard->PSetupData()->UnattendFile))
  1493. {
  1494. hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile,
  1495. NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
  1496. if (SUCCEEDED(hr))
  1497. {
  1498. DWORD dw;
  1499. hr = csif.HrGetDword(c_szAfSectionNetworking, c_szAfBuildNumber, &dw);
  1500. if (SUCCEEDED(hr) && ((wWinNT4BuildNumber == dw) ||
  1501. (wWinNT351BuildNumber == dw)))
  1502. {
  1503. hr = pData->pIdent->GetComputerRole(&dw);
  1504. if (SUCCEEDED(hr) && (dw == GCR_MEMBER))
  1505. {
  1506. //fixupComputerDNSDomainName();
  1507. TraceTag (ttidWizard, "Calling NetpUpgradePreNT5JoinInfo...");
  1508. if (NERR_Success != NetpUpgradePreNT5JoinInfo ())
  1509. {
  1510. TraceHr (
  1511. ttidWizard, FAL, E_FAIL, FALSE,
  1512. "NetpUpgradePreNT5JoinInfo failed. "
  1513. "Likely delay load problem with netapi32.dll");
  1514. }
  1515. }
  1516. }
  1517. }
  1518. }
  1519. TraceHr(ttidWizard, FAL, hr, FALSE, "JoinUpgradeNT351orNT4toNT5");
  1520. }
  1521. //
  1522. // Function: OnJoinPageActivate
  1523. //
  1524. // Purpose:
  1525. //
  1526. // Parameters:
  1527. //
  1528. // Returns:
  1529. //
  1530. BOOL OnJoinPageActivate(HWND hwndDlg)
  1531. {
  1532. TraceFileFunc(ttidGuiModeSetup);
  1533. // Retrieve the CWizard instance from the dialog
  1534. CWizard * pWizard =
  1535. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  1536. Assert(NULL != pWizard);
  1537. JoinData * pData = reinterpret_cast<JoinData *>
  1538. (pWizard->GetPageData(IDD_Join));
  1539. Assert(NULL != pData);
  1540. TraceTag(ttidWizard, "Entering Join page...");
  1541. if (ISDC(ProductType(pWizard)))
  1542. {
  1543. // 412142 : we are going to skip the Join page a little farther down, but
  1544. // even for a DC, we need to do this.
  1545. //
  1546. if (FALSE == pData->fUpgraded)
  1547. {
  1548. JoinUpgradeNT351orNT4toNT5(pWizard, pData);
  1549. pData->fUpgraded = TRUE;
  1550. }
  1551. }
  1552. // mbend 02/08/2000
  1553. //
  1554. // BUG 433915
  1555. // Domain/Workgroup page in setup: Check to by-pass for SBS case
  1556. // If this computer is a domain controller or there are no adapters or we don't
  1557. // want activation, then don't show the Join page.
  1558. //
  1559. if (IsSBS() || ISDC(ProductType(pWizard)) || !pWizard->PAdapterQueue()->FAdaptersInstalled() ||
  1560. (IsRunningOnPersonal() && !(IsUnattended(pWizard) && (FALSE == pData->fUpgraded))) )
  1561. {
  1562. PAGEDIRECTION PageDir = pWizard->GetPageDirection(IDD_Join);
  1563. if (NWPD_FORWARD == PageDir)
  1564. {
  1565. // if forward goto exit page
  1566. //
  1567. pWizard->SetPageDirection(IDD_Join, NWPD_BACKWARD);
  1568. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_Exit);
  1569. }
  1570. else
  1571. {
  1572. // if backward goto upgrade page
  1573. //
  1574. pWizard->SetPageDirection(IDD_Join, NWPD_FORWARD);
  1575. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_Upgrade);
  1576. }
  1577. }
  1578. else // !DC
  1579. {
  1580. // Accept focus
  1581. //
  1582. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 0);
  1583. PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
  1584. // If we're in unattended mode and haven't tried to join a
  1585. // domain/workgroup then give it a try
  1586. //
  1587. if (IsUnattended(pWizard) && (FALSE == pData->fUpgraded))
  1588. {
  1589. HRESULT hr;
  1590. pData->fUpgraded = TRUE;
  1591. // Raid 193450 - NT4 -> NT5 we need to SetComputerNameEx
  1592. //
  1593. JoinUpgradeNT351orNT4toNT5(pWizard, pData);
  1594. // Get the join parameters from the answer file and populate the UI
  1595. //
  1596. hr = HrJoinProcessAnswerFile(hwndDlg, pWizard, pData);
  1597. if (S_FALSE == hr)
  1598. {
  1599. // S_FALSE was returned, this means no answerfile section
  1600. // for identification is present or not all the required
  1601. // data is present. Advance without joining unless we're in
  1602. // defaulthide mode.
  1603. // Advance regardless if we're upgrading, or running on Personal.
  1604. //
  1605. Assert(S_FALSE == hr);
  1606. if (((UM_DEFAULTHIDE == pWizard->GetUnattendedMode()) ||
  1607. (UM_READONLY == pWizard->GetUnattendedMode()))
  1608. && (!IsUpgrade(pWizard))
  1609. && (!IsRunningOnPersonal()) )
  1610. {
  1611. // Pretend something failed so that pressing NEXT will
  1612. // do the join. Basically we just populated the UI from
  1613. // the answerfile.
  1614. //
  1615. pData->fUnattendedFailed = TRUE;
  1616. }
  1617. else
  1618. {
  1619. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1620. }
  1621. }
  1622. else
  1623. {
  1624. if (FAILED(hr))
  1625. {
  1626. if ( IsRunningOnPersonal() )
  1627. {
  1628. // In case of Home Edition, log the error irrespective of the type of
  1629. // unattended mode since we never show domain/workgroup join page.
  1630. NetSetupLogStatusV( LogSevWarning,
  1631. SzLoadIds (IDS_E_UNATTENDED_INVALID_WORKGROUP_ID_SECTION));
  1632. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1633. }
  1634. else
  1635. {
  1636. if ( UM_FULLUNATTENDED == pWizard->GetUnattendedMode() )
  1637. {
  1638. // Raid 380374: no UI allowed if in full unattended mode ..
  1639. NetSetupLogStatusV( LogSevWarning,
  1640. SzLoadIds (IDS_E_UNATTENDED_INVALID_ID_SECTION));
  1641. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1642. }
  1643. else
  1644. {
  1645. // Stop on this page, something was wrong in the answerfile
  1646. //
  1647. pData->fUnattendedFailed = TRUE;
  1648. }
  1649. }
  1650. }
  1651. else
  1652. {
  1653. // If we're in UM_FULLUNATTENDED or in UM_DEFAULTHIDE mode
  1654. // launch the thread which will do the unattended join
  1655. //
  1656. Assert(S_OK == hr);
  1657. if ((UM_FULLUNATTENDED == pWizard->GetUnattendedMode()) ||
  1658. (UM_DEFAULTHIDE == pWizard->GetUnattendedMode()) ||
  1659. (UM_READONLY == pWizard->GetUnattendedMode()))
  1660. {
  1661. OnJoinDoUnattended(hwndDlg);
  1662. }
  1663. else
  1664. {
  1665. // Pretend something failed so that pressing NEXT will
  1666. // do the join. Basically we just populated the UI from
  1667. // the answerfile.
  1668. //
  1669. pData->fUnattendedFailed = TRUE;
  1670. }
  1671. }
  1672. }
  1673. }
  1674. // If something failed in the unattended case or
  1675. // If we are not unattended and we did not process this,
  1676. // make sure the page shows if called from GUI mode setup.
  1677. if (pData->fUnattendedFailed ||
  1678. !IsUnattended(pWizard))
  1679. {
  1680. // Make the page visible if it is not an upgrade, otherwise just continue to the next page.
  1681. if (!IsUpgrade(pWizard))
  1682. {
  1683. // else, make sure the page is visible.
  1684. if (g_pSetupWizard != NULL)
  1685. {
  1686. g_pSetupWizard->PSetupData()->ShowHideWizardPage(TRUE);
  1687. }
  1688. UpdateNextBackBttns(hwndDlg);
  1689. }
  1690. else
  1691. {
  1692. PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L);
  1693. }
  1694. }
  1695. }
  1696. return TRUE;
  1697. }
  1698. //
  1699. // Function: OnJoinInitDialog
  1700. //
  1701. // Purpose:
  1702. //
  1703. // Parameters:
  1704. //
  1705. // Returns:
  1706. //
  1707. BOOL OnJoinInitDialog(HWND hwndDlg, LPARAM lParam)
  1708. {
  1709. TraceFileFunc(ttidGuiModeSetup);
  1710. HRESULT hr;
  1711. CWizard * pWizard;
  1712. JoinData * pData;
  1713. PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
  1714. Assert(psp->lParam);
  1715. ::SetWindowLongPtr(hwndDlg, DWLP_USER, psp->lParam);
  1716. pWizard = reinterpret_cast<CWizard *>(psp->lParam);
  1717. Assert(NULL != pWizard);
  1718. pData = reinterpret_cast<JoinData *>(pWizard->GetPageData(IDD_Join));
  1719. Assert(NULL != pData);
  1720. if(!pData)
  1721. {
  1722. return false;
  1723. }
  1724. // Set the descriptive text
  1725. tstring str = SzLoadIds(IDS_TXT_JOIN_DESC_1);
  1726. str += L"\n";
  1727. str += SzLoadIds(IDS_TXT_JOIN_DESC_2);
  1728. SetWindowText(GetDlgItem(hwndDlg, TXT_JOIN_DESC), str.c_str());
  1729. // Set the maximum length of text in the edit control, and
  1730. // subclass it so when the control has no text the next bttn
  1731. // is disabled.
  1732. HWND hwndEditDomain = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME);
  1733. SendMessage(hwndEditDomain, EM_LIMITTEXT, MAX_DOMAINNAME_LENGTH, 0L);
  1734. ::SetWindowLongPtr(hwndEditDomain, GWLP_USERDATA, ::GetWindowLongPtr(hwndEditDomain, GWLP_WNDPROC));
  1735. ::SetWindowLongPtr(hwndEditDomain, GWLP_WNDPROC, (LONG_PTR)JoinEditSubclassProc);
  1736. HWND hwndEditWorkgroup = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME);
  1737. SendMessage(hwndEditWorkgroup, EM_LIMITTEXT, MAX_WORKGROUPNAME_LENGTH, 0L);
  1738. ::SetWindowLongPtr(hwndEditWorkgroup, GWLP_USERDATA, ::GetWindowLongPtr(hwndEditWorkgroup, GWLP_WNDPROC));
  1739. ::SetWindowLongPtr(hwndEditWorkgroup, GWLP_WNDPROC, (LONG_PTR)JoinEditSubclassProc);
  1740. pData->hwndDlg = hwndDlg;
  1741. // Initialize to Workgroup default
  1742. CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP,
  1743. BTN_JOIN_DOMAIN, BTN_JOIN_WORKGROUP);
  1744. // Get the identification interface
  1745. TraceTag(ttidWizard, "Querying computer role...");
  1746. hr = HrGetIdentInterface(&pData->pIdent);
  1747. if (FAILED(hr))
  1748. {
  1749. Assert(NULL == pData->pIdent);
  1750. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
  1751. PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
  1752. }
  1753. else
  1754. {
  1755. // Update the UI based on the selection
  1756. UpdateJoinUsingComputerRole(hwndDlg, pWizard);
  1757. }
  1758. return FALSE;
  1759. }
  1760. //
  1761. // Function: OnJoinWizBack
  1762. //
  1763. // Purpose:
  1764. //
  1765. // Parameters:
  1766. //
  1767. // Returns:
  1768. //
  1769. BOOL OnJoinWizBack(HWND hwndDlg)
  1770. {
  1771. TraceFileFunc(ttidGuiModeSetup);
  1772. OnProcessPrevAdapterPagePrev(hwndDlg, IDD_Upgrade);
  1773. return TRUE;
  1774. }
  1775. //
  1776. // Function: GetCredentials
  1777. //
  1778. // Purpose: Prompt the user for username and password.
  1779. //
  1780. // Returns: TRUE on success, otherwise FALSE.
  1781. //
  1782. // Author: asinha 5/03/2001
  1783. //
  1784. BOOL GetCredentials (HWND hwndParent, JoinData *pData)
  1785. {
  1786. WCHAR szCaption[CREDUI_MAX_CAPTION_LENGTH+1];
  1787. CREDUI_INFOW uiInfo;
  1788. DWORD dwErr;
  1789. TraceFileFunc(ttidGuiModeSetup);
  1790. DwFormatString(SzLoadIds(IDS_JOIN_DOMAIN_CAPTION), szCaption,
  1791. celems(szCaption), pData->szDomain);
  1792. ZeroMemory( &uiInfo, sizeof(uiInfo) );
  1793. uiInfo.cbSize = sizeof(CREDUI_INFOW);
  1794. uiInfo.hwndParent = hwndParent;
  1795. uiInfo.pszMessageText = SzLoadIds(IDS_JOIN_DOMAIN_TEXT);
  1796. uiInfo.pszCaptionText = szCaption;
  1797. dwErr = CredUIPromptForCredentialsW(
  1798. &uiInfo,
  1799. NULL,
  1800. NULL,
  1801. NO_ERROR,
  1802. pData->szUserName,
  1803. MAX_USERNAME_LENGTH+1,
  1804. pData->szPassword,
  1805. SAM_MAX_PASSWORD_LENGTH+1,
  1806. NULL,
  1807. CREDUI_FLAGS_DO_NOT_PERSIST |
  1808. CREDUI_FLAGS_GENERIC_CREDENTIALS |
  1809. CREDUI_FLAGS_VALIDATE_USERNAME |
  1810. CREDUI_FLAGS_COMPLETE_USERNAME |
  1811. CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
  1812. CREDUI_FLAGS_ALWAYS_SHOW_UI );
  1813. Assert(dwErr != ERROR_INVALID_PARAMETER);
  1814. Assert(dwErr != ERROR_INVALID_FLAGS);
  1815. if (dwErr == ERROR_CANCELLED)
  1816. {
  1817. pData->szUserName[0] = 0;
  1818. pData->szPassword[0] = 0;
  1819. }
  1820. return NO_ERROR == dwErr; // e.g. ERROR_CANCELLED
  1821. }
  1822. //
  1823. // Function: JoinWorkgroupDomain
  1824. //
  1825. // Purpose:
  1826. //
  1827. // Parameters:
  1828. //
  1829. // Returns: nothing
  1830. //
  1831. VOID JoinWorkgroupDomain(HWND hwndDlg, CWizard * pWizard,
  1832. JoinData * pData)
  1833. {
  1834. TraceFileFunc(ttidGuiModeSetup);
  1835. DWORD dwThreadId = 0;
  1836. HANDLE hthrd = NULL;
  1837. Assert(NULL != pData->pIdent);
  1838. Assert(NULL != pData->hwndDlg);
  1839. // Retain the domain/workgroup name. Note that the answerfile
  1840. // populates the UI before this routine is called so requerying
  1841. // from the UI covers both cases (answerfile or user input)
  1842. //
  1843. if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN))
  1844. {
  1845. HWND hwndEdit = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME);
  1846. Assert(0 != GetWindowTextLength(hwndEdit));
  1847. GetWindowText(hwndEdit, pData->szDomain, MAX_DOMAINNAME_LENGTH + 1);
  1848. // If no information was seeded into the structure, prompt for it.
  1849. //
  1850. if (0 == pData->szUserName[0])
  1851. {
  1852. pData->szPassword[0] = 0;
  1853. // Preserve the only Win9x upgrade flag if it was present
  1854. //
  1855. pData->dwJoinFlag &= JDF_WIN9x_UPGRADE;
  1856. // Get the username/password when joining a domain
  1857. //
  1858. BOOL bRet;
  1859. bRet = GetCredentials(GetParent(hwndDlg), pData);
  1860. // Note: Must set return as -1 otherwise join page will advance
  1861. //
  1862. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
  1863. if (bRet == FALSE)
  1864. {
  1865. return;
  1866. }
  1867. }
  1868. }
  1869. else
  1870. {
  1871. HWND hwndEdit = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME);
  1872. Assert(0 != GetWindowTextLength(hwndEdit));
  1873. GetWindowText(hwndEdit, pData->szDomain, MAX_WORKGROUPNAME_LENGTH + 1);
  1874. // Initialize the workstation settings for username/password
  1875. // and join flags
  1876. //
  1877. pData->szUserName[0] = 0;
  1878. pData->dwJoinFlag = 0;
  1879. pData->szPassword[0] = 0;
  1880. MemFree(pData->pszMachineObjectOU);
  1881. pData->pszMachineObjectOU = NULL;
  1882. pData->szComputerPassword[0] = 0;
  1883. }
  1884. // Create the thread to join the workgroup/domain
  1885. hthrd = CreateThread(NULL, STACK_SIZE_TINY,
  1886. (LPTHREAD_START_ROUTINE)JoinDomainWorkThrd,
  1887. (LPVOID)pData, 0, &dwThreadId);
  1888. if (NULL != hthrd)
  1889. {
  1890. SetCursorToHourglass( hwndDlg, pData );
  1891. PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
  1892. EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
  1893. CloseHandle(hthrd);
  1894. }
  1895. else
  1896. {
  1897. // Failed to create the required netsetup thread
  1898. AssertSz(0,"Unable to create JoinWorkgroupDomain thread.");
  1899. TraceHr(ttidWizard, FAL, E_OUTOFMEMORY, FALSE, "JoinWorkgroupDomain - Create thread failed");
  1900. }
  1901. }
  1902. //
  1903. // Function: OnJoinWizNext
  1904. //
  1905. // Purpose:
  1906. //
  1907. // Parameters:
  1908. //
  1909. // Returns:
  1910. //
  1911. BOOL OnJoinWizNext(HWND hwndDlg)
  1912. {
  1913. TraceFileFunc(ttidGuiModeSetup);
  1914. // Retrieve the CWizard instance from the dialog
  1915. CWizard * pWizard =
  1916. reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER));
  1917. Assert(NULL != pWizard);
  1918. JoinData * pData = reinterpret_cast<JoinData *>
  1919. (pWizard->GetPageData(IDD_Join));
  1920. Assert(NULL != pData);
  1921. if(!pData)
  1922. {
  1923. return false;
  1924. }
  1925. // Attempt to join the workgroup/domain if we have the Ident interface
  1926. //
  1927. if (pData->pIdent)
  1928. {
  1929. // Ensure the user supplied a workgroup/domain name
  1930. //
  1931. if (0 == GetWindowTextLength(GetDlgItem(hwndDlg, GetJoinNameIIDFromSelection(hwndDlg))))
  1932. {
  1933. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
  1934. return TRUE;
  1935. }
  1936. // Join the workgroup/domain
  1937. //
  1938. if (!IsUnattended(pWizard) || pData->fUnattendedFailed)
  1939. {
  1940. DNS_STATUS dnsStatus;
  1941. if ( IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN) )
  1942. {
  1943. dnsStatus = IsValidDomainName( hwndDlg );
  1944. }
  1945. else
  1946. {
  1947. dnsStatus = ERROR_SUCCESS;
  1948. }
  1949. if ( (dnsStatus == ERROR_SUCCESS) ||
  1950. (dnsStatus == DNS_ERROR_NON_RFC_NAME) )
  1951. {
  1952. JoinWorkgroupDomain(hwndDlg, pWizard, pData);
  1953. }
  1954. else
  1955. {
  1956. tstring str;
  1957. str = SzLoadIds(IDS_JOIN_E_DOMAIN_INVALID_NAME);
  1958. MessageBox(GetParent(hwndDlg), str.c_str(),
  1959. SzLoadIds(IDS_SETUP_CAPTION), MB_OK);
  1960. }
  1961. }
  1962. ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
  1963. }
  1964. else
  1965. {
  1966. OnJoinSuccess(hwndDlg);
  1967. }
  1968. return TRUE;
  1969. }
  1970. //
  1971. // Function: dlgprocJoin
  1972. //
  1973. // Purpose: Dialog Procedure for the Join wizard page
  1974. //
  1975. // Parameters: standard dlgproc parameters
  1976. //
  1977. // Returns: INT_PTR
  1978. //
  1979. INT_PTR CALLBACK dlgprocJoin( HWND hwndDlg, UINT uMsg,
  1980. WPARAM wParam, LPARAM lParam )
  1981. {
  1982. TraceFileFunc(ttidGuiModeSetup);
  1983. BOOL frt = FALSE;
  1984. switch (uMsg)
  1985. {
  1986. case PWM_JOINFAILURE:
  1987. frt = OnJoinFailure(hwndDlg, lParam);
  1988. break;
  1989. case PWM_JOINSUCCESS:
  1990. frt = OnJoinSuccess(hwndDlg);
  1991. break;
  1992. case WM_INITDIALOG:
  1993. frt = OnJoinInitDialog(hwndDlg, lParam);
  1994. break;
  1995. case WM_COMMAND:
  1996. {
  1997. if ((BN_CLICKED == HIWORD(wParam)) &&
  1998. ((BTN_JOIN_DOMAIN == LOWORD(wParam)) ||
  1999. (BTN_JOIN_WORKGROUP == LOWORD(wParam))))
  2000. {
  2001. JoinUpdatePromptText(hwndDlg);
  2002. }
  2003. }
  2004. break;
  2005. case WM_NOTIFY:
  2006. {
  2007. LPNMHDR pnmh = (LPNMHDR)lParam;
  2008. switch (pnmh->code)
  2009. {
  2010. // propsheet notification
  2011. case PSN_HELP:
  2012. break;
  2013. case PSN_SETACTIVE:
  2014. frt = OnJoinPageActivate(hwndDlg);
  2015. break;
  2016. case PSN_APPLY:
  2017. break;
  2018. case PSN_KILLACTIVE:
  2019. break;
  2020. case PSN_RESET:
  2021. break;
  2022. case PSN_WIZBACK:
  2023. frt = OnJoinWizBack(hwndDlg);
  2024. break;
  2025. case PSN_WIZFINISH:
  2026. break;
  2027. case PSN_WIZNEXT:
  2028. frt = OnJoinWizNext(hwndDlg);
  2029. break;
  2030. default:
  2031. break;
  2032. }
  2033. }
  2034. break;
  2035. default:
  2036. break;
  2037. }
  2038. return( frt );
  2039. }
  2040. //
  2041. // Function: JoinPageCleanup
  2042. //
  2043. // Purpose: As a callback function to allow any page allocated memory
  2044. // to be cleaned up, after the page will no longer be accessed.
  2045. //
  2046. // Parameters: pWizard [IN] - The wizard against which the page called
  2047. // register page
  2048. // lParam [IN] - The lParam supplied in the RegisterPage call
  2049. //
  2050. // Returns: nothing
  2051. //
  2052. VOID JoinPageCleanup(CWizard *pWizard, LPARAM lParam)
  2053. {
  2054. TraceFileFunc(ttidGuiModeSetup);
  2055. JoinData * pData = reinterpret_cast<JoinData *>(lParam);
  2056. if (NULL != pData)
  2057. {
  2058. delete pData->pIdent;
  2059. }
  2060. MemFree(reinterpret_cast<void*>(lParam));
  2061. }
  2062. //
  2063. // Function: CreateJoinPage
  2064. //
  2065. // Purpose: To determine if the Join page needs to be shown, and to
  2066. // to create the page if requested. Note the Join page is
  2067. // responsible for initial installs also.
  2068. //
  2069. // Parameters: pWizard [IN] - Ptr to a Wizard instance
  2070. // pData [IN] - Context data to describe the world in
  2071. // which the Wizard will be run
  2072. // fCountOnly [IN] - If True, only the maximum number of
  2073. // pages this routine will create need
  2074. // be determined.
  2075. // pnPages [IN] - Increment by the number of pages
  2076. // to create/created
  2077. //
  2078. // Returns: HRESULT, S_OK on success
  2079. //
  2080. HRESULT HrCreateJoinPage(CWizard *pWizard, PINTERNAL_SETUP_DATA pData,
  2081. BOOL fCountOnly, UINT *pnPages)
  2082. {
  2083. TraceFileFunc(ttidGuiModeSetup);
  2084. HRESULT hr = S_OK;
  2085. // Batch Mode or for fresh install
  2086. if (!IsPostInstall(pWizard))
  2087. {
  2088. // If not only counting, create and register the page
  2089. if (!fCountOnly)
  2090. {
  2091. JoinData * pData = NULL;
  2092. HPROPSHEETPAGE hpsp;
  2093. PROPSHEETPAGE psp;
  2094. TraceTag(ttidWizard, "Creating Join Page");
  2095. hr = E_OUTOFMEMORY;
  2096. pData = reinterpret_cast<JoinData *>(MemAlloc(sizeof(JoinData)));
  2097. if (pData)
  2098. {
  2099. pData->fUnattendedFailed = FALSE;
  2100. pData->fUpgraded = FALSE;
  2101. pData->pIdent = NULL;
  2102. pData->hOldCursor = NULL;
  2103. pData->hwndDlg = NULL;
  2104. pData->dwJoinFlag = 0;
  2105. pData->szUserName[0] = 0;
  2106. pData->szPassword[0] = 0;
  2107. pData->szDomain[0] = 0;
  2108. pData->pszMachineObjectOU = NULL;
  2109. pData->szComputerPassword[0] = 0;
  2110. psp.dwSize = sizeof( PROPSHEETPAGE );
  2111. psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
  2112. psp.hInstance = _Module.GetResourceInstance();
  2113. psp.pszTemplate = MAKEINTRESOURCE( IDD_Join );
  2114. psp.hIcon = NULL;
  2115. psp.pfnDlgProc = dlgprocJoin;
  2116. psp.lParam = reinterpret_cast<LPARAM>(pWizard);
  2117. psp.pszHeaderTitle = SzLoadIds(IDS_T_Join);
  2118. psp.pszHeaderSubTitle = SzLoadIds(IDS_ST_Join);
  2119. hpsp = CreatePropertySheetPage( &psp );
  2120. if (hpsp)
  2121. {
  2122. pWizard->RegisterPage(IDD_Join, hpsp,
  2123. JoinPageCleanup,
  2124. reinterpret_cast<LPARAM>(pData));
  2125. hr = S_OK;
  2126. }
  2127. else
  2128. {
  2129. MemFree(pData);
  2130. }
  2131. }
  2132. }
  2133. if (SUCCEEDED(hr))
  2134. {
  2135. (*pnPages)++;
  2136. }
  2137. }
  2138. TraceHr(ttidWizard, FAL, hr, FALSE, "HrCreateJoinPage");
  2139. return hr;
  2140. }
  2141. //
  2142. // Function: AppendJoinPage
  2143. //
  2144. // Purpose: Add the Join page, if it was created, to the set of pages
  2145. // that will be displayed.
  2146. //
  2147. // Parameters: pWizard [IN] - Ptr to Wizard Instance
  2148. // pahpsp [IN,OUT] - Array of pages to add our page to
  2149. // pcPages [IN,OUT] - Count of pages in pahpsp
  2150. //
  2151. // Returns: Nothing
  2152. //
  2153. VOID AppendJoinPage(CWizard *pWizard, HPROPSHEETPAGE* pahpsp, UINT *pcPages)
  2154. {
  2155. TraceFileFunc(ttidGuiModeSetup);
  2156. if (!IsPostInstall(pWizard))
  2157. {
  2158. HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_Join);
  2159. Assert(hPage);
  2160. pahpsp[*pcPages] = hPage;
  2161. (*pcPages)++;
  2162. }
  2163. }