#include "setupp.h" #pragma hdrstop // // List of characters that are not legal in netnames. // PCWSTR IllegalNetNameChars = L"\"/\\[]:|<>+=;,?*"; // // Computer name. // WCHAR ComputerName[DNS_MAX_LABEL_LENGTH+1]; WCHAR Win32ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; BOOL IsNameTruncated; BOOL IsNameNonRfc; // // Copy disincentive name/organization strings. // WCHAR NameOrgName[MAX_NAMEORG_NAME+1]; WCHAR NameOrgOrg[MAX_NAMEORG_ORG+1]; #ifdef DOLOCALUSER // // User name and password // WCHAR UserName[MAX_USERNAME+1]; WCHAR UserPassword[MAX_PASSWORD+1]; BOOL CreateUserAccount = FALSE; #endif // def DOLOCALUSER // // Administrator password. // WCHAR CurrentAdminPassword[MAX_PASSWORD+1]; WCHAR AdminPassword[MAX_PASSWORD+1]; BOOL EncryptedAdminPasswordSet = FALSE; BOOL DontChangeAdminPassword = FALSE; VOID GenerateName( OUT PWSTR GeneratedString, IN DWORD DesiredStrLen ) { static DWORD Seed = 98725757; static PCWSTR UsableChars = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // // How many characters will come from the org/name string. // DWORD BaseLength = 8; DWORD i,j; DWORD UsableCount; if( DesiredStrLen < BaseLength ) { BaseLength = DesiredStrLen - 1; } if( NameOrgOrg[0] ) { wcscpy( GeneratedString, NameOrgOrg ); } else if( NameOrgName[0] ) { wcscpy( GeneratedString, NameOrgName ); } else { wcscpy( GeneratedString, TEXT("X") ); for( i = 1; i < BaseLength; i++ ) { wcscat( GeneratedString, TEXT("X") ); } } // // Get him upper-case for our filter... // CharUpper(GeneratedString); // // Now we want to put a '-' at the end // of our GeneratedString. We'd like it to // be placed in the BASE_LENGTH character, but // the string may be shorter than that, or may // even have a ' ' in it. Figure out where to // put the '-' now. // for( i = 0; i <= BaseLength; i++ ) { // // Check for a short string. // if( (GeneratedString[i] == 0 ) || (GeneratedString[i] == L' ') || (!wcschr(UsableChars, GeneratedString[i])) || (i == BaseLength ) ) { GeneratedString[i] = L'-'; GeneratedString[i+1] = 0; break; } } // // Special case the scenario where we had no usable // characters. // if( GeneratedString[0] == L'-' ) { GeneratedString[0] = 0; } UsableCount = lstrlen(UsableChars); Seed ^= GetCurrentTime(); srand( Seed ); j = lstrlen( GeneratedString ); for( i = j; i < DesiredStrLen; i++ ) { GeneratedString[i] = UsableChars[rand() % UsableCount]; } GeneratedString[i] = 0; // // In the normal case, the edit control in the wizard page // has the ES_UPPER bit set. In the normal unattend case // there is code in unattend.txt that uppercases the name. // But if we get to generating the name then then the text // in unattend.txt was * and we thus never had any text in // the edit control or unattend.txt to be uppercased. // CharUpper(GeneratedString); } BOOL ContainsDot( IN PCWSTR NameToCheck ) /*++ Routine Description: Determine whether a given name contains a '.' Arguments: NameToCheck - supplies name to be checked. Return Value: TRUE if the name contains a '.'; FALSE if not. --*/ { UINT Length,u; if (!NameToCheck) return FALSE; Length = lstrlen(NameToCheck); for (u = 0; u < Length; u++) { if (NameToCheck[u] == L'.') return TRUE; } return FALSE; } BOOL IsNetNameValid( IN PCWSTR NameToCheck, IN BOOL AlphaNumericOnly ) /*++ Routine Description: Determine whether a given name is valid as a netname, such as a computer name. Arguments: NameToCheck - supplies name to be checked. Return Value: TRUE if the name is valid; FALSE if not. --*/ { UINT Length,u; Length = lstrlen(NameToCheck); // // Want at least one character. // if(!Length) { return(FALSE); } // // Leading/trailing spaces are invalid. // if((NameToCheck[0] == L' ') || (NameToCheck[Length-1] == L' ')) { return(FALSE); } // // Control chars are invalid, as are characters in the illegal chars list. // for(u=0; ucode) { case PSN_SETACTIVE: TESTHOOK(503); BEGIN_SECTION(L"Personalize Your Software Page"); SetWizardButtons(hdlg,WizPageNameOrg); if (Unattended) { if (!UnattendSetActiveDlg(hdlg,IDD_NAMEORG)) { break; } } // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); // // Set focus on the name edit control. // SetFocus(GetDlgItem(hdlg,IDT_NAME)); // // Open/Close IME. // SetIMEOpenStatus(hdlg,TRUE); break; case PSN_WIZNEXT: case PSN_WIZFINISH: UnattendAdvanceIfValid (hdlg); // see WMX_VALIDATE break; case PSN_KILLACTIVE: WizardKillHelp(hdlg); SetWindowLongPtr(hdlg, DWLP_MSGRESULT, FALSE); // // Close IME. // SetIMEOpenStatus(hdlg,FALSE); END_SECTION(L"Personalize Your Software Page"); break; case PSN_HELP: WizardBringUpHelp(hdlg,WizPageNameOrg); break; default: break; } break; default: return(FALSE); } return(TRUE); } INT_PTR CALLBACK ComputerNameDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { NMHDR *NotifyParams; DWORD err, Win32NameSize = MAX_COMPUTERNAME_LENGTH + 1; static BOOL EncryptedAdminPasswordBad = FALSE; static BOOL bPersonal = FALSE; switch(msg) { case WM_INITDIALOG: { bPersonal = ( GetProductFlavor() == 4); // // Limit text to maximum length // SendDlgItemMessage(hdlg,IDT_EDIT1,EM_LIMITTEXT,DNS_MAX_LABEL_LENGTH,0); if (!bPersonal) { SendDlgItemMessage(hdlg,IDT_EDIT2,EM_LIMITTEXT,MAX_PASSWORD,0); SendDlgItemMessage(hdlg,IDT_EDIT3,EM_LIMITTEXT,MAX_PASSWORD,0); } // // Set the Edit boxes to the initial text // // // Generate a computer name if we're unattended and // the user has requested a random name, or if we're // attended. // GenerateName( ComputerName, 15 ); if( (Unattended) && (UnattendAnswerTable[UAE_COMPNAME].Answer.String) && (UnattendAnswerTable[UAE_COMPNAME].Answer.String[0] == L'*') ) { // // The unattend engine has asked us to generate a Computer // name. Let's write the data back into the unattend // database. // MyFree( UnattendAnswerTable[UAE_COMPNAME].Answer.String ); UnattendAnswerTable[UAE_COMPNAME].Answer.String = ComputerName; } if (!bPersonal) { if(DontChangeAdminPassword) { EnableWindow(GetDlgItem(hdlg,IDT_EDIT2),FALSE); EnableWindow(GetDlgItem(hdlg,IDT_EDIT3),FALSE); } else { SetDlgItemText(hdlg,IDT_EDIT2,AdminPassword); SetDlgItemText(hdlg,IDT_EDIT3,AdminPassword); } } break; } case WM_IAMVISIBLE: MessageBoxFromMessage( hdlg, ComputerName[0] ? MSG_BAD_COMPUTER_NAME1 : MSG_BAD_COMPUTER_NAME2, NULL, IDS_ERROR,MB_OK|MB_ICONSTOP); break; case WM_SIMULATENEXT: // Simulate the next button somehow PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT); break; case WMX_VALIDATE: // // lParam == 0 for no UI, or 1 for UI // Return 1 for success, -1 for error. // IsNameNonRfc = FALSE; IsNameTruncated = FALSE; GetDlgItemText(hdlg,IDT_EDIT1,ComputerName,DNS_MAX_LABEL_LENGTH+1); // StrTrim removes both leading and trailing spaces StrTrim(ComputerName, TEXT(" ")); if (ContainsDot(ComputerName)) { err = ERROR_INVALID_NAME; } else { err = DnsValidateDnsName_W(ComputerName); if (err == DNS_ERROR_NON_RFC_NAME) { IsNameNonRfc = TRUE; err = ERROR_SUCCESS; } if(err == ERROR_SUCCESS) { //The name is a valid DNS name. Now verify that it is //also a valid WIN32 computer name. if (!DnsHostnameToComputerNameW(ComputerName, Win32ComputerName, &Win32NameSize) || !IsNetNameValid(Win32ComputerName, FALSE)) { err = ERROR_INVALID_NAME; } else { if (Win32NameSize < (UINT)lstrlen(ComputerName) ) { //The DNSName was truncated to get a Win32 ComputerName. IsNameTruncated = TRUE; } } } } // // If the name has non-RFC characters or if it was truncated, warn // user if it is not an unattended install and we have GUI. // if (err == ERROR_SUCCESS && !Unattended && lParam) { if (IsNameNonRfc) { //ComputerName has non-standard characters. if (MessageBoxFromMessage( hdlg, MSG_DNS_NON_RFC_NAME, NULL, IDS_SETUP,MB_YESNO|MB_ICONWARNING, ComputerName) == IDNO) { SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); SendDlgItemMessage(hdlg,IDT_EDIT1,EM_SETSEL,0,-1); return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } } if (IsNameTruncated) { //The computer name was truncated. if (MessageBoxFromMessage( hdlg, MSG_DNS_NAME_TRUNCATED, NULL, IDS_SETUP,MB_YESNO|MB_ICONWARNING, ComputerName, Win32ComputerName) == IDNO) { SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); SendDlgItemMessage(hdlg,IDT_EDIT1,EM_SETSEL,0,-1); return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } } } if(err == ERROR_SUCCESS) { WCHAR pw1[MAX_PASSWORD+1],pw2[MAX_PASSWORD+1]; if (bPersonal) { // if we are in personal we are done. return ReturnDlgResult (hdlg, VALIDATE_DATA_OK); } // // Good computer name. Now make sure passwords match. // GetDlgItemText(hdlg,IDT_EDIT2,pw1,MAX_PASSWORD+1); GetDlgItemText(hdlg,IDT_EDIT3,pw2,MAX_PASSWORD+1); if(lstrcmp(pw1,pw2)) { // // Skip UI? // if (!lParam) { return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } // // // Inform user of password mismatch, and don't allow next page // to be activated. // if (Unattended) { UnattendErrorDlg(hdlg, IDD_COMPUTERNAME); } MessageBoxFromMessage(hdlg,MSG_PW_MISMATCH,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP); SetDlgItemText(hdlg,IDT_EDIT2,L""); SetDlgItemText(hdlg,IDT_EDIT3,L""); SetFocus(GetDlgItem(hdlg,IDT_EDIT2)); return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } else { WCHAR adminName[MAX_USERNAME+1]; GetAdminAccountName( adminName ); // // Process the encrypted password if // 1) We are unattended and // 2) The EncryptedAdminPassword key is "Yes" in the unatttend file and // 3) We are not back here after setting it - i.e. via the back button etc. // 4) We are not back here after failing to set it once. if( Unattended && IsEncryptedAdminPasswordPresent() && !DontChangeAdminPassword && !EncryptedAdminPasswordBad){ // Logging is done inside the call to ProcessEncryptedAdminPassword if(!(ProcessEncryptedAdminPassword(adminName))){ EncryptedAdminPasswordBad = TRUE; // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); // Inform the user and enable the password fields UnattendErrorDlg(hdlg, IDD_COMPUTERNAME); MessageBoxFromMessage(hdlg,MSG_CHANGING_PW_FAIL,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP, adminName ); SetDlgItemText(hdlg,IDT_EDIT2,L""); SetDlgItemText(hdlg,IDT_EDIT3,L""); SetFocus(GetDlgItem(hdlg,IDT_EDIT2)); return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); }else{ EncryptedAdminPasswordSet = TRUE; // // Set DontChangeAdminPassword to avoid the user from ever trying to // reset the password using the dialog. This is needed in the case where the // unattend fails, say, in the next page and the user gets here using the back button. // DontChangeAdminPassword = TRUE; } }else{ // // They match; allow next page to be activated. // if (Unattended && pw1[0] == L'*') { pw1[0] = L'\0'; } // Set administrator password. We need to do some checking here though. // There are 3 scenarios that can occur in mini-setup: // 1. The OEM doesn't want to have the admin password changed. // In this case, he's set OEMAdminPassword = "NoChange". If that's // what we find, we don't assign the password. Remember that this // system has already been installed, so there's already an admin // password. // 2. The OEM wants to set the admin password to a specific string. // In this case, he's set OEMAdminPassword = . // If this is the case, we've already caught this string in the // wizard page. // 3. The OEM wants to let the user set the admin password. In this // case, there's no OEMAdminpassword in the answer file. If this // is the case, we've already caught this and gotten a password // from the user in the wizard page. // // The good news is that the unattend engine has already looked // for a password in the unattend file called "NoChange" and has // set a global called "DontChangeAdminPassword" to indicate. if(!DontChangeAdminPassword) { lstrcpy(AdminPassword,pw1); // // The user may have changed the name of the Administrator // account. We'll call some special code to retrieve the // name on the account. This is really only needed in the // case of a sysprep run, but it can't hurt to do it always. // // In the Win9x case their code in winnt32 generates a random // password and passes it to us through the unattend file and // so we set it here and do the right thing. // For Minisetup the behavior for now is to silently fail setting // the admin password if there was an existing Password on the system. // WE only allow setting the admin password from NULL i.e. one time change. // In any other case we log the error and move on. // if(!SetLocalUserPassword(adminName,CurrentAdminPassword,AdminPassword) && !MiniSetup) { SetupDebugPrint( L"SETUP: SetLocalUserPassword failed" ); // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); MessageBoxFromMessage(hdlg,MSG_CHANGING_PW_FAIL,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP, adminName ); SetDlgItemText(hdlg,IDT_EDIT2,L""); SetDlgItemText(hdlg,IDT_EDIT3,L""); SetFocus(GetDlgItem(hdlg,IDT_EDIT2)); return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } // // Now store this so that we work fine when the user comes to this page by hitting "Back". // lstrcpy( CurrentAdminPassword, AdminPassword ); } } } } else { // // Skip UI? // if (!lParam) { return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } // // Inform user of bogus computer name, and don't allow next page // to be activated. // if (Unattended) { UnattendErrorDlg(hdlg, IDD_COMPUTERNAME); } MessageBoxFromMessage( hdlg, ComputerName[0] ? MSG_BAD_COMPUTER_NAME1 : MSG_BAD_COMPUTER_NAME2, NULL, IDS_ERROR,MB_OK|MB_ICONSTOP ); SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); SendDlgItemMessage(hdlg,IDT_EDIT1,EM_SETSEL,0,-1); return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); } return ReturnDlgResult (hdlg, VALIDATE_DATA_OK); case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch(NotifyParams->code) { case PSN_SETACTIVE: TESTHOOK(504); BEGIN_SECTION(L"Computer Name Page"); SetWizardButtons(hdlg,WizPageComputerName); // // Load ComputerName because it may have been set when the user // entered the organization name. // SetDlgItemText(hdlg,IDT_EDIT1,ComputerName); if(Unattended && !UnattendSetActiveDlg(hdlg,IDD_COMPUTERNAME)) { break; } // // Post ourselves a message we'll get once displayed. // PostMessage(hdlg,WM_USER,0,0); break; case PSN_WIZBACK: // // Save ComputerName because we're going to load it into the dialog // again when we come back. // GetDlgItemText(hdlg,IDT_EDIT1,ComputerName,DNS_MAX_LABEL_LENGTH+1); break; case PSN_WIZNEXT: case PSN_WIZFINISH: UnattendAdvanceIfValid (hdlg); // see WMX_VALIDATE break; case PSN_KILLACTIVE: WizardKillHelp(hdlg); SetWindowLongPtr(hdlg, DWLP_MSGRESULT, FALSE); END_SECTION(L"Computer Name Page"); break; case PSN_HELP: WizardBringUpHelp(hdlg,WizPageComputerName); break; default: break; } break; case WM_USER: // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); // // Select the computer name string and set focus to it. // SendDlgItemMessage(hdlg,IDT_EDIT1,EM_SETSEL,0,-1); SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); break; default: return(FALSE); } return(TRUE); } #ifdef DOLOCALUSER BOOL CheckUserAccountData( IN HWND hdlg, OUT BOOL ValidateOnly ) { WCHAR userName[MAX_USERNAME+1]; WCHAR pw1[MAX_PASSWORD+1]; WCHAR pw2[MAX_PASSWORD+1]; WCHAR adminName[MAX_USERNAME+1]; WCHAR guestName[MAX_USERNAME+1]; UINT MessageId; FocusId = 0; // // Load names of built-in accounts. // LoadString(MyModuleHandle,IDS_ADMINISTRATOR,adminName,MAX_USERNAME+1); LoadString(MyModuleHandle,IDS_GUEST,guestName,MAX_USERNAME+1); // // Fetch data user typed in for username and password. // GetDlgItemText(hdlg,IDT_EDIT1,userName,MAX_USERNAME+1); GetDlgItemText(hdlg,IDT_EDIT2,pw1,MAX_PASSWORD+1); GetDlgItemText(hdlg,IDT_EDIT3,pw2,MAX_PASSWORD+1); if(lstrcmpi(userName,adminName) && lstrcmpi(userName,guestName)) { if(userName[0]) { if(IsNetNameValid(userName,FALSE)) { if(lstrcmp(pw1,pw2)) { // // Passwords don't match. // MessageId = MSG_PW_MISMATCH; SetDlgItemText(hdlg,IDT_EDIT2,L""); SetDlgItemText(hdlg,IDT_EDIT3,L""); SetFocus(GetDlgItem(hdlg,IDT_EDIT2)); } else { // // Name is non-empty, is not a built-in, is valid, // and the passwords match. // MessageId = 0; } } else { // // Name is not valid. // MessageId = MSG_BAD_USER_NAME1; SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); } } else { // // Don't allow empty name. // MessageId = MSG_BAD_USER_NAME2; SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); } } else { // // User entered name of a built-in account. // MessageId = MSG_BAD_USER_NAME3; SetFocus(GetDlgItem(hdlg,IDT_EDIT1)); } if(MessageId && !ValidateOnly) { MessageBoxFromMessage(hdlg,MessageId,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP); } return(MessageId == 0); } BOOL CALLBACK UserAccountDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { NMHDR *NotifyParams; switch(msg) { case WM_INITDIALOG: // // Limit text to maximum length of a user account name, // and limit password text to max langth of a password. // Also set initial text. // SendDlgItemMessage(hdlg,IDT_EDIT1,EM_LIMITTEXT,MAX_USERNAME,0); SendDlgItemMessage(hdlg,IDT_EDIT2,EM_LIMITTEXT,MAX_PASSWORD,0); SendDlgItemMessage(hdlg,IDT_EDIT3,EM_LIMITTEXT,MAX_PASSWORD,0); SetDlgItemText(hdlg,IDT_EDIT1,UserName); SetDlgItemText(hdlg,IDT_EDIT2,UserPassword); SetDlgItemText(hdlg,IDT_EDIT3,UserPassword); break; case WM_SIMULATENEXT: // Simulate the next button somehow PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT); break; case WMX_VALIDATE: // // lParam == 0 for no UI, or 1 for UI // Return 1 for success, -1 for error. // // // Check name. // if(CheckUserAccountData(hdlg, lParam == 0)) { // // Data is valid. Move on to next page. // GetDlgItemText(hdlg,IDT_EDIT1,UserName,MAX_USERNAME+1); GetDlgItemText(hdlg,IDT_EDIT2,UserPassword,MAX_PASSWORD+1); CreateUserAccount = TRUE; } else if (Unattended) { // // Data is invalid but we're unattended, so just don't create // the account. // CreateUserAccount = FALSE; GetDlgItemText(hdlg,IDT_EDIT1,UserName,MAX_USERNAME+1); SetDlgItemText(hdlg,IDT_EDIT2,L""); SetDlgItemText(hdlg,IDT_EDIT3,L""); UserPassword[0] = 0; return ReturnDlgResult (hdlg, VALIDATE_DATA_OK); } // // Don't allow next page to be activated. // return ReturnDlgResult (hdlg, VALIDATE_DATA_INVALID); case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch(NotifyParams->code) { case PSN_SETACTIVE: TESTHOOK(505); BEGIN_SECTION(L"User Name and Password Page"); SetWizardButtons(hdlg,WizPageUserAccount); // // Load ComputerName because it may have been set when the user // entered the user name. // SetDlgItemText(hdlg,IDT_EDIT1,UserName); // // Always activate in ui test mode // if(!UiTest) { // // Don't activate if this is a dc server or Win9x upgrade. // if(ISDC(ProductType) || Win95Upgrade) { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); break; } } if (Unattended) { if (!UnattendSetActiveDlg(hdlg,IDD_USERACCOUNT)) { break; } } // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); break; case PSN_WIZBACK: // // Save UserName because we're going to load it into the dialog // again when we come back. // GetDlgItemText(hdlg,IDT_EDIT1,UserName,MAX_USERNAME+1); break; case PSN_WIZNEXT: case PSN_WIZFINISH: UnattendAdvanceIfValid (hdlg); // see WMX_VALIDATE break; case PSN_KILLACTIVE: WizardKillHelp(hdlg); SetWindowLongPtr( hdlg, DWLP_MSGRESULT, FALSE ); END_SECTION(L"User Name and Password Page"); break; case PSN_HELP: WizardBringUpHelp(hdlg,WizPageUserAccount); break; default: break; } break; default: return(FALSE); } return(TRUE); } #endif //def DOLOCALUSER