#include "precomp.h" #pragma hdrstop #include #include #include "digpid.h" #define SETUP_TYPE_BUFFER_LEN 8 #define MAX_PID30_SITE 3 #define MAX_PID30_RPC 5 TCHAR Pid30Rpc[7] = TEXT("00000"); TCHAR Pid30Site[4]; LONG SourceInstallType = RetailInstall; BOOL EulaComplete = TRUE; #ifdef PRERELEASE BOOL NoPid = FALSE; #endif // // global variable used for subclassing. // WNDPROC OldPidEditProc[5]; WNDPROC OldEulaEditProc; static BOOL PidMatchesMedia; BOOL ValidatePid30( LPTSTR Edit1, LPTSTR Edit2, LPTSTR Edit3, LPTSTR Edit4, LPTSTR Edit5 ); void GetPID(); VOID ShowPidBox(); VOID GetSourceInstallType( OUT OPTIONAL LPDWORD InstallVariation ) /*++ Routine Description: Determines the installation type (by looking in setupp.ini in the source directory) Arguments: Installvaration - one of the install variations defined in compliance.h Returns: none. sets SourceInstallType global variable. --*/ { TCHAR TypeBuffer[256]; TCHAR FilePath[MAX_PATH]; DWORD InstallVar = COMPLIANCE_INSTALLVAR_UNKNOWN; TCHAR MPCode[6] = { -1 }; // // SourcePaths is guaranteed to be valid at this point, so just use it // lstrcpy(FilePath,NativeSourcePaths[0]); ConcatenatePaths (FilePath, SETUPP_INI, MAX_PATH ); GetPrivateProfileString(PID_SECTION, PID_KEY, TEXT(""), TypeBuffer, sizeof(TypeBuffer)/sizeof(TCHAR), FilePath); if (lstrlen(TypeBuffer)==SETUP_TYPE_BUFFER_LEN) { if (lstrcmp(&TypeBuffer[5], OEM_INSTALL_RPC) == 0) { SourceInstallType = OEMInstall; InstallVar = COMPLIANCE_INSTALLVAR_OEM; } else if (lstrcmp(&TypeBuffer[5], SELECT_INSTALL_RPC) == 0) { SourceInstallType = SelectInstall; InstallVar = COMPLIANCE_INSTALLVAR_SELECT; // Since Select also requires a PID, don't zero the PID and call. /* // get/set the pid. { TCHAR Temp[5][ MAX_PID30_EDIT + 1 ]; Temp[0][0] = TEXT('\0'); ValidatePid30(Temp[0],Temp[1],Temp[2],Temp[3],Temp[4]); }*/ } else if (lstrcmp(&TypeBuffer[5], MSDN_INSTALL_RPC) == 0) { SourceInstallType = RetailInstall; InstallVar = COMPLIANCE_INSTALLVAR_MSDN; } else { // defaulting SourceInstallType = RetailInstall; InstallVar = COMPLIANCE_INSTALLVAR_CDRETAIL; } StringCchCopy(Pid30Site, ARRAYSIZE(Pid30Site), &TypeBuffer[5]); StringCchCopy(Pid30Rpc, 6, TypeBuffer); Pid30Rpc[MAX_PID30_RPC] = (TCHAR)0; } else { // // the retail install doesn't have an RPC code in the PID, so it's shorter in length // SourceInstallType = RetailInstall; InstallVar = COMPLIANCE_INSTALLVAR_CDRETAIL; } if (lstrlen(TypeBuffer) >= 5) { StringCchCopy(MPCode, 6, TypeBuffer); if ( (lstrcmp(MPCode, EVAL_MPC) == 0) || (lstrcmp(MPCode, DOTNET_EVAL_MPC) == 0)) { InstallVar = COMPLIANCE_INSTALLVAR_EVAL; } else if ((lstrcmp(MPCode, SRV_NFR_MPC) == 0) || (lstrcmp(MPCode, ASRV_NFR_MPC) == 0)) { InstallVar = COMPLIANCE_INSTALLVAR_NFR; } } if (InstallVariation){ *InstallVariation = InstallVar; } } BOOL SetPid30( HWND hdlg, LONG ExpectedPidType, LPTSTR pProductId ) /*++ Routine Description: sets the pid in the wizard page to the data specified in the answer file. Arguments: hdlg - window handle to pid dialog ExpectedPidType - InstallType enum identifying what sort of pid we're looking for. pProductId - string passed in from unattend file Returns: true on successfully setting the data, false means the data was missing or invalid. may set some the dialog text in the specified dialog --*/ { TCHAR *ptr; TCHAR Temp[5][ MAX_PID30_EDIT + 1 ]; UINT i; // // make sure we were provided with a product ID // if (!pProductId || !*pProductId) { return(FALSE); } if ( (ExpectedPidType != RetailInstall) && (ExpectedPidType != OEMInstall) && (ExpectedPidType != SelectInstall) ){ return(FALSE); } // // OEM and cd retail are the same case // Check that the string specified on the unattended script file // represents a valid 25 digit product id: // // 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 // 0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 // // As a first validation test, we verify that the length is correct, // then we check if the "-" characters are in the correct place // // Note - we rely on left to right evaluation, so that we don't access // pProductId[23] if length is only 5. if( ( lstrlen( pProductId ) != (4+ MAX_PID30_EDIT*5)) || ( pProductId[5] != (TCHAR)TEXT('-') ) || ( pProductId[11] != (TCHAR)TEXT('-') ) || ( pProductId[17] != (TCHAR)TEXT('-') ) || ( pProductId[23] != (TCHAR)TEXT('-') ) ) { // // The Pid in the unattended script file is invalid. // return(FALSE); } for (i = 0;i<5;i++) { // // quintet i // ptr = &pProductId[i*(MAX_PID30_EDIT+1)]; StringCchCopy(Temp[i], MAX_PID30_EDIT+1, ptr); Temp[i][MAX_PID30_EDIT] = (TCHAR)'\0'; } // // check with pid30 to make sure it's valid // if (!ValidatePid30(Temp[0],Temp[1],Temp[2],Temp[3],Temp[4])) { return(FALSE); } // // all of the specified pid items are valid, set the dialog text and return. // SetDlgItemText( hdlg,IDT_EDIT_PID1, Temp[0] ); SetDlgItemText( hdlg,IDT_EDIT_PID2, Temp[1] ); SetDlgItemText( hdlg,IDT_EDIT_PID3, Temp[2] ); SetDlgItemText( hdlg,IDT_EDIT_PID4, Temp[3] ); SetDlgItemText( hdlg,IDT_EDIT_PID5, Temp[4] ); return(TRUE); } // // This function assumes CdKey points to a buffer of 16 bytes. // BOOL pGetCdKey ( OUT PBYTE CdKey ) { DIGITALPID dpid; DWORD type; DWORD rc; HKEY key; DWORD size = sizeof (dpid); BOOL b = FALSE; rc = RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), &key); if (rc == ERROR_SUCCESS) { rc = RegQueryValueEx (key, TEXT("DigitalProductId"), NULL, &type, (LPBYTE)&dpid, &size); if (rc == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(dpid)) { CopyMemory (CdKey, &dpid.abCdKey, sizeof (dpid.abCdKey)); b = TRUE; } RegCloseKey (key); } return b; } const unsigned int iBase = 24; // // obtained from Jim Harkins 11/27/2000 // void EncodePid3g( TCHAR *pchCDKey3Chars, // [OUT] pointer to 29+1 character Secure Product key LPBYTE pbCDKey3) // [IN] pointer to 15-byte binary Secure Product Key { // Given the binary PID 3.0 we need to encode // it into ASCII characters. We're only allowed to // use 24 characters so we need to do a base 2 to // base 24 conversion. It's just like any other // base conversion execpt the numbers are bigger // so we have to do the long division ourselves. const TCHAR achDigits[] = TEXT("BCDFGHJKMPQRTVWXY2346789"); int iCDKey3Chars = 29; int cGroup = 0; pchCDKey3Chars[iCDKey3Chars--] = TEXT('\0'); while (0 <= iCDKey3Chars) { unsigned int i = 0; // accumulator int iCDKey3; for (iCDKey3 = 15-1; 0 <= iCDKey3; --iCDKey3) { i = (i * 256) + pbCDKey3[iCDKey3]; pbCDKey3[iCDKey3] = (BYTE)(i / iBase); i %= iBase; } // i now contains the remainder, which is the current digit pchCDKey3Chars[iCDKey3Chars--] = achDigits[i]; // add '-' between groups of 5 chars if (++cGroup % 5 == 0 && iCDKey3Chars > 0) { pchCDKey3Chars[iCDKey3Chars--] = TEXT('-'); } } return; } LRESULT CALLBACK PidEditSubProc( IN HWND hwnd, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Edit control subclass routine, sets the focus to the correct edit box when the user enters text. This routine assumes that the pid controls ids are in sequential order. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { DWORD len, id; // // eat spaces // if ((msg == WM_CHAR) && (wParam == VK_SPACE)) { return(0); } if ((msg == WM_CHAR)) { // // First override: if we have the max characters in the current edit // box, let's post the character to the next box and set focus to that // control. // if ( ( (len = (DWORD)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)) == MAX_PID30_EDIT) && ((wParam != VK_DELETE) && (wParam != VK_BACK)) ) { // // set the focus to the next edit control and post the character // to that edit control // if ((id = GetDlgCtrlID(hwnd)) < IDT_EDIT_PID5 ) { DWORD start, end; SendMessage(hwnd, EM_GETSEL, (WPARAM)&start,(LPARAM)&end); if (start == end) { HWND hNext = GetDlgItem(GetParent(hwnd),id+1); SetFocus(hNext); SendMessage(hNext, EM_SETSEL, (WPARAM)-1,(LPARAM)-1); PostMessage( GetDlgItem(GetParent(hwnd),id+1), WM_CHAR, wParam, lParam ); return(0); } } // // Second override: if the user hit's a delete key and they are at the // the start of an edit box, then post the delete to the previous edit // box. // } else if ( (len == 0) && ((id = GetDlgCtrlID(hwnd)) > IDT_EDIT_PID1) && ((wParam == VK_DELETE) || (wParam == VK_BACK) )) { // // set the focus to the previous edit control and post the command // to that edit control // HWND hPrev = GetDlgItem(GetParent(hwnd),id-1); SetFocus(hPrev); SendMessage(hPrev, EM_SETSEL, (WPARAM)MAX_PID30_EDIT-1,(LPARAM)MAX_PID30_EDIT); PostMessage( hPrev, WM_CHAR, wParam, lParam ); return(0); // // Third override: if posting this message will give us the maximum // characters in our in the current edit box, let's post the character // to the next box and set focus to that control. // } else if ( (len == MAX_PID30_EDIT-1) && ((wParam != VK_DELETE) && (wParam != VK_BACK)) && ((id = GetDlgCtrlID(hwnd)) < IDT_EDIT_PID5) ) { DWORD start, end; SendMessage(hwnd, EM_GETSEL, (WPARAM)&start,(LPARAM)&end); if (start == end) { HWND hNext = GetDlgItem(GetParent(hwnd),id+1); // // post the message to the edit box // CallWindowProc(OldPidEditProc[GetDlgCtrlID(hwnd)-IDT_EDIT_PID1],hwnd,msg,wParam,lParam); // // now set the focus to the next edit control // SetFocus(hNext); SendMessage(hNext, EM_SETSEL, (WPARAM)-1,(LPARAM)-1); return(0); } } } return(CallWindowProc(OldPidEditProc[GetDlgCtrlID(hwnd)-IDT_EDIT_PID1],hwnd,msg,wParam,lParam)); } LRESULT CALLBACK EulaEditSubProc( IN HWND hwnd, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Edit control subclass routine, to avoid highlighting text when user tabs to the edit control. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { static BOOL firstTime = TRUE; // // For setsel messages, make start and end the same. // if((msg == EM_SETSEL) && ((LPARAM)wParam != lParam)) { lParam = wParam; } // // also, if the user hits tab, set focus to the correct radio button // after the first time, the tab starts working like we want it to // if ((msg == WM_KEYDOWN) && (wParam == VK_TAB) && firstTime) { firstTime = FALSE; if (! ((IsDlgButtonChecked(GetParent(hwnd), IDYES) == BST_CHECKED) || (IsDlgButtonChecked(GetParent(hwnd), IDNO ) == BST_CHECKED))) { CheckDlgButton( GetParent(hwnd),IDYES, BST_CHECKED ); PostMessage(GetParent(hwnd),WM_COMMAND,IDYES,0); SetFocus(GetDlgItem(GetParent(hwnd),IDYES)); } } return(CallWindowProc(OldEulaEditProc,hwnd,msg,wParam,lParam)); } LPTSTR DoInitializeEulaText( HWND EditControl, PBOOL TranslationProblem ) /*++ Routine Description: retrieves the text out of eula.txt, and sets up the eula subclass routine Arguments: EditControl - window handle for edit control TranslationProblem - if we fail, was is because we couldn't translate the text? Returns: pointer to eula text so that it can be freed, NULL on failure --*/ { TCHAR EulaPath[MAX_PATH]; DWORD err; HANDLE hFile, hFileMapping; DWORD FileSize; BYTE *pbFile; LPTSTR EulaText = NULL; int i; // HFONT hFont; // accoding to MSDN LOCALE_IDEFAULTANSICODEPAGE and LOCALE_IDEFAULTCODEPAGE // are a max of 6 characters. Unsure of whether or not this includes ending // null character, and we need a leading _T('.') TCHAR cpName[8]; if (TranslationProblem) { *TranslationProblem = FALSE; } // // Map the file containing the licensing agreement. // lstrcpy(EulaPath, NativeSourcePaths[0]); // // Make sure this operation succeeds. We don't want to possibly load the wrong eula.txt! // if (!ConcatenatePaths (EulaPath, TEXT("eula.txt"), MAX_PATH )) { goto c0; } // // Open and map the inf file. // err = MapFileForRead(EulaPath,&FileSize,&hFile,&hFileMapping,&pbFile); if(err != NO_ERROR) { goto c0; } if(FileSize == 0xFFFFFFFF) { goto c1; } EulaText = MALLOC((FileSize+1) * sizeof(TCHAR)); if(EulaText == NULL) { goto c1; } #ifdef UNICODE // the Eula will be in the language of the build, so we should set out locale // to use the codepage that the source wants. if(!GetLocaleInfo(SourceNativeLangID,LOCALE_IDEFAULTANSICODEPAGE,&cpName[1], ( ( sizeof( cpName ) / sizeof( TCHAR ) ) - 1 ) )){ if(!GetLocaleInfo(SourceNativeLangID,LOCALE_IDEFAULTCODEPAGE,&cpName[1], ( ( sizeof( cpName ) / sizeof( TCHAR ) ) - 1 ) )){ FREE(EulaText); EulaText = NULL; if (TranslationProblem){ *TranslationProblem = TRUE; } goto c1; } } cpName[0] = _T('.'); _tsetlocale(LC_ALL,cpName); // // Translate the text from ANSI to Unicode. // if(!mbstowcs(EulaText,pbFile,FileSize)){ FREE(EulaText); EulaText = NULL; if (TranslationProblem) { *TranslationProblem = TRUE; } goto c1; } _tsetlocale(LC_ALL,_T("")); /* // we use mbstowcs instead of MultiByteToWideChar because mbstowcs will // take into account the code page of the locale, which we've just set // to be in relation to the language of the build if (!MultiByteToWideChar ( CP_ACP, MB_ERR_INVALID_CHARS, pbFile, FileSize, EulaText, (FileSize+1) * sizeof(WCHAR) ) ) { FREE( EulaText ); EulaText = NULL; if (TranslationProblem) { *TranslationProblem = TRUE; } goto c1; } */ #else CopyMemory(EulaText, pbFile, FileSize); #endif // // add the trailing NULL character // EulaText[FileSize] = 0; // // setup the eula subclass // OldEulaEditProc = (WNDPROC)GetWindowLongPtr(EditControl,GWLP_WNDPROC); SetWindowLongPtr(EditControl,GWLP_WNDPROC,(LONG_PTR)EulaEditSubProc); #if 0 // // need a fixed width font for the EULA so it's formatted correctly for all resolutions // hFont = GetStockObject(SYSTEM_FIXED_FONT); if (hFont) { SendMessage( EditControl, WM_SETFONT, hFont, TRUE ); } #endif // // set the actual text // SetWindowText(EditControl,(LPCTSTR)EulaText); c1: UnmapFile( hFileMapping, pbFile ); CloseHandle( hFile ); c0: return EulaText; } BOOL EulaWizPage( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Eula wizard page. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { BOOL b; PPAGE_RUNTIME_DATA WizPage = (PPAGE_RUNTIME_DATA)GetWindowLongPtr(hdlg,DWLP_USER); static LPTSTR EulaText = NULL; static BOOL ShowEula = TRUE; BOOL TranslationProblem; CHECKUPGRADEONLY(); switch(msg) { case WM_INITDIALOG: // // check if the current language and target language match // ShowEula = IsLanguageMatched; // // Set eula radio buttons. // CheckDlgButton( hdlg,IDYES, BST_UNCHECKED ); CheckDlgButton( hdlg,IDNO, BST_UNCHECKED ); // // setup the eula // EulaText = DoInitializeEulaText( GetDlgItem( hdlg, IDT_EULA_LIC_TEXT ), &TranslationProblem ); // // if we couldn't read the eula, check if it was because of a translation problem, // in which case we defer to textmode setup // if (!EulaText && TranslationProblem == TRUE) { ShowEula = FALSE; } // // if this fails, only bail out if we were going to show the EULA in the first place. // if (!EulaText && ShowEula) { MessageBoxFromMessage( hdlg, MSG_EULA_FAILED, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); Cancelled = TRUE; PropSheet_PressButton(GetParent(hdlg),PSBTN_CANCEL); b = FALSE; } PropSheet_SetWizButtons( GetParent(hdlg), WizPage->CommonData.Buttons & (~PSWIZB_NEXT) ); // // Set focus to radio button // SetFocus(GetDlgItem(hdlg,IDYES)); b = FALSE; break; case WM_COMMAND: if (wParam == IDYES) { PropSheet_SetWizButtons( GetParent(hdlg), WizPage->CommonData.Buttons | PSWIZB_NEXT ); b = TRUE; } else if (wParam == IDNO) { PropSheet_SetWizButtons( GetParent(hdlg), WizPage->CommonData.Buttons | PSWIZB_NEXT ); b = TRUE; } else b = FALSE; break; case WMX_ACTIVATEPAGE: b = TRUE; if(wParam) { // // don't activate the page in restart mode // if (Winnt32RestartedWithAF ()) { EulaComplete = TRUE; return FALSE; } // // activation // if (!ShowEula) { // // the target install language and the source language do not match up // since this means that we might not have fonts installed for the current // language, we'll just defer this to textmode setup where we know we have // the correct fonts // EulaComplete = FALSE; if (IsDlgButtonChecked(hdlg, IDYES) == BST_CHECKED) { PropSheet_PressButton(GetParent(hdlg), (lParam == PSN_WIZBACK) ? PSBTN_BACK : PSBTN_NEXT); } else { CheckDlgButton( hdlg,IDYES, BST_CHECKED ); PropSheet_PressButton(GetParent(hdlg),PSBTN_NEXT); } return(b); } // // set state of next button if user has backed up and reentered this dialog // if ( (IsDlgButtonChecked(hdlg, IDYES) == BST_CHECKED) || (IsDlgButtonChecked(hdlg, IDNO ) == BST_CHECKED) ) { PropSheet_SetWizButtons( GetParent(hdlg), WizPage->CommonData.Buttons | PSWIZB_NEXT ); } else { SendMessage(GetParent(hdlg), PSM_SETWIZBUTTONS, 0, (LPARAM)WizPage->CommonData.Buttons & (~PSWIZB_NEXT)); } // // If unattended, check the value of OemSkipEula first (NTBUG9:492934) // if (UnattendedScriptFile) { TCHAR Buffer[10]; if (GetPrivateProfileString( WINNT_UNATTENDED, TEXT("OemSkipEula"), TEXT(""), Buffer, sizeof(Buffer)/sizeof(Buffer[0]), UnattendedScriptFile )) { EulaComplete = lstrcmpi(Buffer,WINNT_A_YES) == 0; return FALSE; } } // // Advance page in unattended case. // UNATTENDED(PSBTN_NEXT); } else { // // deactivation // if (EulaText) FREE( EulaText ); EulaText = NULL; if (IsDlgButtonChecked(hdlg, IDNO ) == BST_CHECKED) { Cancelled = TRUE; PropSheet_PressButton(GetParent(hdlg),PSBTN_CANCEL); } } break; case WMX_UNATTENDED: // // necessary? // if (EulaText) FREE( EulaText ); EulaText = NULL; b = FALSE; break; case WMX_I_AM_VISIBLE: // // Force repainting first to make sure the page is visible. // InvalidateRect(hdlg,NULL,FALSE); UpdateWindow(hdlg); b = TRUE; break; default: b = FALSE; break; } return(b); } BOOL SelectPid30WizPage( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: OEM Pid wizard page. depends on SourceInstallType being set correctly. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { BOOL b; PPAGE_RUNTIME_DATA WizPage = (PPAGE_RUNTIME_DATA)GetWindowLongPtr(hdlg,DWLP_USER); static BOOL bUnattendPid = FALSE; DWORD i; switch(msg) { case WM_INITDIALOG: GetPID(); // Disable the IME on the PID edit controls for (i = 0; i < 5;i++) { ImmAssociateContext(GetDlgItem(hdlg, IDT_EDIT_PID1+i), (HIMC)NULL); } // // subclass the edit controls and limit the number of characters // for (i = 0; i < 5;i++) { SendDlgItemMessage(hdlg,IDT_EDIT_PID1+i,EM_LIMITTEXT,MAX_PID30_EDIT,0); OldPidEditProc[i] = (WNDPROC)GetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC); SetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC,(LONG_PTR)PidEditSubProc); } // // set focus to first pid entry // SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); b = FALSE; break; case WMX_ACTIVATEPAGE: CHECKUPGRADEONLY(); if (BuildCmdcons) { return(FALSE); } // If we have an ecrypted PID and don't have the right crypto installed // defer the PID validation until GUI mode if (g_bDeferPIDValidation) { return FALSE; } b = TRUE; if(wParam) { // // activation // #ifdef PRERELEASE if (NoPid) { // // don't show the page in this case // b = FALSE; break; } #endif if (SourceInstallType != SelectInstall) { // // don't show the page in this case // b = FALSE; break; } // // don't activate the page in restart mode // if (Winnt32RestartedWithAF ()) { if (GetPrivateProfileString ( WINNT_USERDATA, WINNT_US_PRODUCTKEY, TEXT(""), ProductId, sizeof (ProductId) / sizeof (ProductId[0]), g_DynUpdtStatus->RestartAnswerFile )) { return FALSE; } } if (UnattendedOperation) { // // make sure the pid is specified in the unattend file else we should stop // ShowPidBox(hdlg, SW_HIDE); if (SetPid30(hdlg,SourceInstallType, (LPTSTR)&ProductId) ) { UNATTENDED(PSBTN_NEXT); } else { // // a hack so that the correct wizard page is active when we put up our message box // bUnattendPid = TRUE; ShowPidBox(hdlg, SW_SHOW); PostMessage(hdlg,WMX_I_AM_VISIBLE,0,0); } } } else { // // deactivation. don't verify anything if they are backing up // if (!Cancelled && lParam != PSN_WIZBACK) { TCHAR tmpBuffer1[6]; TCHAR tmpBuffer2[6]; TCHAR tmpBuffer3[6]; TCHAR tmpBuffer4[6]; TCHAR tmpBuffer5[6]; GetDlgItemText(hdlg,IDT_EDIT_PID1,tmpBuffer1,sizeof(tmpBuffer1)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID2,tmpBuffer2,sizeof(tmpBuffer2)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID3,tmpBuffer3,sizeof(tmpBuffer3)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID4,tmpBuffer4,sizeof(tmpBuffer4)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID5,tmpBuffer5,sizeof(tmpBuffer5)/sizeof(TCHAR)); b = ValidatePid30( tmpBuffer1, tmpBuffer2, tmpBuffer3, tmpBuffer4, tmpBuffer5 ); if (!b) { if (UnattendedOperation) { // We should not fail ValidatePid30 if we succeeded // in the SetPid30 above. If we failed in SetPid30 above // we should already be showing the pid boxes. ShowPidBox(hdlg, SW_SHOW); } MessageBoxFromMessage(hdlg, bUnattendPid ? MSG_UNATTEND_OEM_PID_IS_INVALID : MSG_OEM_PID_IS_INVALID, FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); b = FALSE; } else { // // user entered a valid PID, save it for later on. // wsprintf( ProductId, TEXT("%s-%s-%s-%s-%s"), tmpBuffer1, tmpBuffer2, tmpBuffer3, tmpBuffer4, tmpBuffer5 ); } } } break; case WMX_I_AM_VISIBLE: // // Force repainting first to make sure the page is visible. // InvalidateRect(hdlg,NULL,FALSE); UpdateWindow(hdlg); if (bUnattendPid) { MessageBoxFromMessage(hdlg,MSG_UNATTEND_OEM_PID_IS_INVALID,FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); bUnattendPid = FALSE; } b = TRUE; break; default: b = FALSE; break; } return(b); } BOOL OemPid30WizPage( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: OEM Pid wizard page. depends on SourceInstallType being set correctly. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { BOOL b; PPAGE_RUNTIME_DATA WizPage = (PPAGE_RUNTIME_DATA)GetWindowLongPtr(hdlg,DWLP_USER); static BOOL bUnattendPid = FALSE; DWORD i; // HFONT hFont; switch(msg) { case WM_INITDIALOG: GetPID(); // hFont = GetStockObject(SYSTEM_FIXED_FONT); // Disable the IME on the PID edit controls for (i = 0; i < 5;i++) { ImmAssociateContext(GetDlgItem(hdlg, IDT_EDIT_PID1+i), (HIMC)NULL); } // // subclass the edit controls and limit the number of characters // for (i = 0; i < 5;i++) { SendDlgItemMessage(hdlg,IDT_EDIT_PID1+i,EM_LIMITTEXT,MAX_PID30_EDIT,0); // SendDlgItemMessage(hdlg, IDT_EDIT_PID1+i, WM_SETFONT, hFont, TRUE ); OldPidEditProc[i] = (WNDPROC)GetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC); SetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC,(LONG_PTR)PidEditSubProc); } // // set focus to first pid entry // SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); b = FALSE; break; case WM_COMMAND: b = FALSE; break; case WMX_ACTIVATEPAGE: CHECKUPGRADEONLY(); if (BuildCmdcons) { return(FALSE); } // If we have an ecrypted PID and don't have the right crypto installed // defer the PID validation until GUI mode if (g_bDeferPIDValidation) { return FALSE; } b = TRUE; if(wParam) { // // activation // #ifdef PRERELEASE if (NoPid) { // // don't show the page in this case // b = FALSE; break; } #endif if (SourceInstallType != OEMInstall) { // // don't show the page in this case // b = FALSE; break; } // // don't activate the page in restart mode // if (Winnt32RestartedWithAF ()) { if (GetPrivateProfileString ( WINNT_USERDATA, WINNT_US_PRODUCTKEY, TEXT(""), ProductId, sizeof (ProductId) / sizeof (ProductId[0]), g_DynUpdtStatus->RestartAnswerFile )) { return FALSE; } } if (UnattendedOperation) { // // make sure the pid is specified in the unattend file else we should stop // if (SetPid30(hdlg,SourceInstallType, (LPTSTR)&ProductId) ) { UNATTENDED(PSBTN_NEXT); } else { // // a hack so that the correct wizard page is active when we put up our message box // bUnattendPid = TRUE; PostMessage(hdlg,WMX_I_AM_VISIBLE,0,0); } } #if 0 if (!Upgrade || (SourceInstallType != OEMInstall)) { // // don't show the page in this case // b = FALSE; break; } else { NOTHING; } #endif } else { // // deactivation. don't verify anything if they are backing up // if (!Cancelled && lParam != PSN_WIZBACK) { TCHAR tmpBuffer1[6]; TCHAR tmpBuffer2[6]; TCHAR tmpBuffer3[6]; TCHAR tmpBuffer4[6]; TCHAR tmpBuffer5[6]; GetDlgItemText(hdlg,IDT_EDIT_PID1,tmpBuffer1,sizeof(tmpBuffer1)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID2,tmpBuffer2,sizeof(tmpBuffer2)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID3,tmpBuffer3,sizeof(tmpBuffer3)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID4,tmpBuffer4,sizeof(tmpBuffer4)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID5,tmpBuffer5,sizeof(tmpBuffer5)/sizeof(TCHAR)); b = ValidatePid30( tmpBuffer1, tmpBuffer2, tmpBuffer3, tmpBuffer4, tmpBuffer5 ); if (!b) { MessageBoxFromMessage(hdlg, bUnattendPid ? MSG_UNATTEND_OEM_PID_IS_INVALID : MSG_OEM_PID_IS_INVALID, FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); b = FALSE; } else { // // user entered a valid PID, save it for later on. // wsprintf( ProductId, TEXT("%s-%s-%s-%s-%s"), tmpBuffer1, tmpBuffer2, tmpBuffer3, tmpBuffer4, tmpBuffer5 ); } } } break; case WMX_I_AM_VISIBLE: // // Force repainting first to make sure the page is visible. // InvalidateRect(hdlg,NULL,FALSE); UpdateWindow(hdlg); if (bUnattendPid) { MessageBoxFromMessage(hdlg,MSG_UNATTEND_OEM_PID_IS_INVALID,FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); bUnattendPid = FALSE; } b = TRUE; break; default: b = FALSE; break; } return(b); } BOOL CdPid30WizPage( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: CD Retail pid wizard page. depends on SourceInstallType being set correctly. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { BOOL b; PPAGE_RUNTIME_DATA WizPage = (PPAGE_RUNTIME_DATA)GetWindowLongPtr(hdlg,DWLP_USER); static BOOL bUnattendPid = FALSE; DWORD i; // HFONT hFont; switch(msg) { case WM_INITDIALOG: GetPID(); // hFont = GetStockObject(SYSTEM_FIXED_FONT); // Disable the IME on the PID edit controls for (i = 0; i < 5;i++) { ImmAssociateContext(GetDlgItem(hdlg, IDT_EDIT_PID1+i), (HIMC)NULL); } // // subclass the edit controls and limit the number of characters // for (i = 0; i < 5;i++) { SendDlgItemMessage(hdlg,IDT_EDIT_PID1+i,EM_LIMITTEXT,MAX_PID30_EDIT,0); // SendDlgItemMessage(hdlg, IDT_EDIT_PID1+i, WM_SETFONT, hFont, TRUE ); OldPidEditProc[i] = (WNDPROC)GetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC); SetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC,(LONG_PTR)PidEditSubProc); } // // set focus to first pid entry // SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); b = FALSE; break; case WM_COMMAND: // // nothing to do here // b = FALSE; break; case WMX_ACTIVATEPAGE: CHECKUPGRADEONLY(); if (BuildCmdcons) { return(FALSE); } // If we have an ecrypted PID and don't have the right crypto installed // defer the PID validation until GUI mode if (g_bDeferPIDValidation) { return FALSE; } b = TRUE; if(wParam) { // // activation // #ifdef PRERELEASE if (NoPid) { // // don't show the page in this case // b = FALSE; break; } #endif if (SourceInstallType != RetailInstall) { // // don't show the page in this case // b = FALSE; break; } // // don't activate the page in restart mode // if (Winnt32RestartedWithAF ()) { if (GetPrivateProfileString ( WINNT_USERDATA, WINNT_US_PRODUCTKEY, TEXT(""), ProductId, sizeof (ProductId) / sizeof (ProductId[0]), g_DynUpdtStatus->RestartAnswerFile )) { return FALSE; } } if (UnattendedOperation) { // // make sure the pid is specified in the unattend file else we should stop // if (SetPid30(hdlg,SourceInstallType, (LPTSTR)&ProductId)) { UNATTENDED(PSBTN_NEXT); } else { // // a hack so that the correct wizard page is active when we put up our message box // bUnattendPid = TRUE; PostMessage(hdlg,WMX_I_AM_VISIBLE,0,0); } } } else { // // deactivation. don't verify anything if they are backing up // if ( !Cancelled && lParam != PSN_WIZBACK ) { TCHAR tmpBuffer1[6]; TCHAR tmpBuffer2[6]; TCHAR tmpBuffer3[6]; TCHAR tmpBuffer4[6]; TCHAR tmpBuffer5[6]; GetDlgItemText(hdlg,IDT_EDIT_PID1,tmpBuffer1,sizeof(tmpBuffer1)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID2,tmpBuffer2,sizeof(tmpBuffer2)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID3,tmpBuffer3,sizeof(tmpBuffer3)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID4,tmpBuffer4,sizeof(tmpBuffer4)/sizeof(TCHAR)); GetDlgItemText(hdlg,IDT_EDIT_PID5,tmpBuffer5,sizeof(tmpBuffer5)/sizeof(TCHAR)); b = ValidatePid30( tmpBuffer1, tmpBuffer2, tmpBuffer3, tmpBuffer4, tmpBuffer5 ); if (!b) { if (PidMatchesMedia){ MessageBoxFromMessage(hdlg, bUnattendPid ? MSG_UNATTEND_CD_PID_IS_INVALID :MSG_CD_PID_IS_INVALID, FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); } else { MessageBoxFromMessage(hdlg, UpgradeOnly ? MSG_CCP_MEDIA_FPP_PID : MSG_FPP_MEDIA_CCP_PID, FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); } SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); b = FALSE; } else { // // user entered a valid PID, save it for later on. // wsprintf( ProductId, TEXT("%s-%s-%s-%s-%s"), tmpBuffer1, tmpBuffer2, tmpBuffer3, tmpBuffer4, tmpBuffer5 ); } } } break; case WMX_I_AM_VISIBLE: // // Force repainting first to make sure the page is visible. // InvalidateRect(hdlg,NULL,FALSE); UpdateWindow(hdlg); if (bUnattendPid) { MessageBoxFromMessage(hdlg,MSG_UNATTEND_CD_PID_IS_INVALID,FALSE,AppTitleStringId,MB_OK|MB_ICONSTOP); bUnattendPid = FALSE; } b = TRUE; break; default: b = FALSE; break; } return(b); } BOOL ValidatePidEx(LPTSTR PID, BOOL *pbStepup, BOOL *bSelect) { TCHAR Pid20Id[MAX_PATH]; BYTE Pid30[1024]={0}; TCHAR pszSkuCode[10]; BOOL fStepUp; // it seems that sku code really doesn't matter in winnt32, only syssetup lstrcpy(pszSkuCode,TEXT("1797XYZZY")); #if 0 wsprintf(DebugBuffer, TEXT("cd-key: %s\nRPC: %s\nOEM Key: %d"), PID, Pid30Rpc, (SourceInstallType == OEMInstall) ); OutputDebugString(DebugBuffer); #endif *(LPDWORD)Pid30 = sizeof(Pid30); if (!SetupPIDGenEx( PID, // [IN] 25-character Secure CD-Key (gets U-Cased) Pid30Rpc, // [IN] 5-character Release Product Code // note sku code is not kept around in winnt32, only syssetup. pszSkuCode, // [IN] Stock Keeping Unit (formatted like 123-12345) (SourceInstallType == OEMInstall), // [IN] is this an OEM install? Pid20Id, // [OUT] PID 2.0, pass in ptr to 24 character array Pid30, // [OUT] pointer to binary PID3 buffer. First DWORD is the length pbStepup, // [OUT] optional ptr to Compliance Checking flag (can be NULL) bSelect // [OUT] optional ptr to Volume Licensing flag (can be NULL) )) { if (g_EncryptedPID) { GlobalFree(g_EncryptedPID); g_EncryptedPID = NULL; } return(FALSE); } return TRUE; } BOOL ValidatePid30( LPTSTR Edit1, LPTSTR Edit2, LPTSTR Edit3, LPTSTR Edit4, LPTSTR Edit5 ) { TCHAR tmpProductId[MAX_PATH]={0}; TCHAR Pid20Id[MAX_PATH]; BYTE Pid30[1024]={0}; TCHAR pszSkuCode[10]; BOOL fStepUp; //TCHAR DebugBuffer[1024]; // until we know better, assume the Pid matches the media type PidMatchesMedia = TRUE; if (!Edit1 || !Edit2 || !Edit3 || !Edit4 || !Edit5) { return(FALSE); } // Since we now need a PID in the select case too, fill in the string. /* if (SourceInstallType == SelectInstall){ tmpProductId[0] = TEXT('\0'); } else */ { StringCchPrintf( tmpProductId, ARRAYSIZE(tmpProductId), TEXT("%s-%s-%s-%s-%s"), Edit1, Edit2, Edit3, Edit4, Edit5 ); } if (!ValidatePidEx(tmpProductId, &fStepUp, NULL)) { return(FALSE); } if (SourceInstallType != OEMInstall){ // we want OEM FPP and CCP keys to be accepted by either media. It seems like // there will be OEM CCP media, but only FPP keys, which is why we aren't // checking to make sure they match, as it's broken by design. if (UpgradeOnly != fStepUp){ // user is trying to do a clean install with upgrade only media. Bad user, bad. PidMatchesMedia = FALSE; return FALSE; } } return(TRUE); } void GetPID() { if (!ProductId[0] && UnattendedOperation && !g_bDeferPIDValidation){ // // On upgrades, reuse existing DPID // BYTE abCdKey[16]; BOOL bDontCare, bSelect; if (Upgrade && ISNT() && OsVersionNumber >= 501 && // PID format is compatible pGetCdKey (abCdKey) ) { EncodePid3g (ProductId, abCdKey); if (ValidatePidEx(ProductId, &bDontCare, &bSelect) && bSelect) { HRESULT hr; if (g_EncryptedPID) { GlobalFree(g_EncryptedPID); g_EncryptedPID = NULL; } // Prepare the encrypted PID so that we can write it to winnt.sif hr = PrepareEncryptedPID(ProductId, 1, &g_EncryptedPID); if (hr != S_OK) { DebugLog (Winnt32LogInformation, TEXT("PrepareEncryptedPID failed: "), 0, hr); } } } } } VOID ShowPidBox( IN HWND hdlg, IN int nCmdShow ) /*++ Routine Description: shows or hide the pid boxes in the wizard page Arguments: hdlg - window handle to pid dialog nCmdShow - SW_SHOW or SW_HIDE --*/ { int i; for (i = 0; i<5; i++) { ShowWindow(GetDlgItem(hdlg,IDT_EDIT_PID1+i), nCmdShow); } }