/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    pid.c

Abstract:

    Product id routines.

Author:

    Ted Miller (tedm) 6-Feb-1995

Revision History:

    13-Sep-1995 (t-stepl) - Check for unattended install

--*/

#include "setupp.h"
#include <spidgen.h>
#include <pencrypt.h>
#pragma hdrstop

CDTYPE  CdType;

//
// Constants used for logging that are specific to this source file.
//
PCWSTR szPidKeyName                 = L"SYSTEM\\Setup\\Pid";
PCWSTR szPidListKeyName             = L"SYSTEM\\Setup\\PidList";
PCWSTR szPidValueName               = L"Pid";
PCWSTR szPidSelectId                = L"270";
#if 0
// msdn no longer exists.
PCWSTR szPidMsdnId                  = L"335";
#endif
PCWSTR szPidOemId                   = L"OEM";
PCWSTR szFinalPidKeyName            = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
PCWSTR szFinalPidValueName          = L"ProductId";
PCWSTR szSkuProfessionalFPP     = L"B23-00079";
PCWSTR szSkuProfessionalCCP     = L"B23-00082";
PCWSTR szSkuProfessionalSelect      = L"B23-00305";
PCWSTR szSkuProfessionalEval        = L"B23-00084";
PCWSTR szSkuServerFPP           = L"C11-00016";
PCWSTR szSkuServerCCP           = L"C11-00027";
PCWSTR szSkuServerSelect        = L"C11-00222";
PCWSTR szSkuServerEval          = L"C11-00026";
PCWSTR szSkuServerNFR           = L"C11-00025";
PCWSTR szSkuAdvServerFPP        = L"C10-00010";
PCWSTR szSkuAdvServerCCP        = L"C10-00015";
PCWSTR szSkuAdvServerSelect     = L"C10-00098";
PCWSTR szSkuAdvServerEval       = L"C10-00014";
PCWSTR szSkuAdvServerNFR        = L"C10-00013";
PCWSTR szSkuDTCFPP          = L"C49-00001";
PCWSTR szSkuDTCSelect           = L"C49-00023";
PCWSTR szSkuUnknown         = L"A22-00001";
PCWSTR szSkuOEM             = L"OEM-93523";

PCWSTR szMSG_LOG_PID_CANT_WRITE_PID = L"Setup was unable to save the Product Id in the registry. Error code = %1!u!.";

//
// Flag indicating whether to display the product id dialog.
//
BOOL DisplayPidDialog = TRUE;

//
// Product ID.
//
WCHAR ProductId[MAX_PRODUCT_ID+1];


PWSTR*  Pid20Array = NULL;

//
// pid 30 product id
//
WCHAR Pid30Text[5][MAX_PID30_EDIT+1];
WCHAR ProductId20FromProductId30[MAX_PRODUCT_ID+1];
WCHAR Pid30Rpc[MAX_PID30_RPC+1];
WCHAR Pid30Site[MAX_PID30_SITE+1];
BYTE  DigitalProductId[DIGITALPIDMAXLEN];

//
// global variable used for subclassing.
//
WNDPROC OldPidEditProc[5];


//
//  Pid related flags
//
// BOOL DisplayPidCdDialog;
// BOOL DisplayPidOemDialog;

//
// forward declarations
//

CDTYPE
MiniSetupGetCdType(
    LPCWSTR Value
    )

/*++

Routine Description:

    Get the right CD type during Mini-Setup. PidGen changes the channel ID
    for the value at HKLM\Software\Microsoft\Windows NT\CurrentVersion!ProductId,
    we have to preserve and rely on the value at HKLM\SYSTEM\Setup\Pid!Pid

Return Value:

    the CdType.

--*/

{
    CDTYPE RetVal;
    WCHAR  TmpPid30Site[MAX_PID30_SITE+1];
    HKEY   Key = NULL;
    DWORD  cbData;
    WCHAR  Data[ MAX_PATH + 1];
    DWORD  Type;

    cbData = sizeof(Data);
    if ( ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                szPidKeyName,
                0,
                KEY_READ,
                &Key ) == ERROR_SUCCESS ) &&
          ( RegQueryValueEx( Key,
                 szPidValueName,
                 0,
                 &Type,
                 ( LPBYTE )Data,
                 &cbData ) == ERROR_SUCCESS ) )
    {
        wcsncpy(TmpPid30Site, Data + MAX_PID30_RPC, MAX_PID30_SITE+1);
    }
    else
    {
        if (Value != NULL)
        {
            wcsncpy(TmpPid30Site, Value, MAX_PID30_SITE+1);
        }
        else
        {
            TmpPid30Site[0] = L'\0';
        }
    }
    
    TmpPid30Site[MAX_PID30_SITE] = (WCHAR)'\0';

    if (_wcsicmp( TmpPid30Site, szPidSelectId ) == 0) {
        RetVal = CDSelect;
    } else if( _wcsicmp( TmpPid30Site, szPidOemId ) == 0 ) {
        RetVal = CDOem;
    } else {
        RetVal = CDRetail;
    }

    if (Key != NULL)
    {
        RegCloseKey(Key);
    }

    return RetVal;
}

BOOL
ValidateAndSetPid30(
    VOID
    )
/*++

Routine Description:

    Using the Pid30Text global variables, check if we have a valid id.
    This generates the pid30 digital product id and pid20 string id, which
    we set into DigitalProductId and ProductId20FromProductId30 globals

Arguments:

    None.

Return Value:

    TRUE if pid was valid.  Set's the globals correctly on success, zero's them out on failure

--*/

{
    WCHAR tmpPid30String[5+ 5*MAX_PID30_EDIT];
    BOOL rc;
    PCWSTR pszSkuCode;

    // Since we require a PID in the Select media too, we need to fill the string
/*
    if (CDSelect == CdType){
        tmpPid30String[0] = L'\0';
    }
    else
*/
    {
    wsprintf(tmpPid30String, L"%s-%s-%s-%s-%s",
         Pid30Text[0],Pid30Text[1],Pid30Text[2],Pid30Text[3],Pid30Text[4]);
    }

    // check for eval
    if (!_wcsicmp(Pid30Rpc,L"82503")){
    // this is eval media ...
    if (ProductType == PRODUCT_WORKSTATION){
        pszSkuCode = szSkuProfessionalEval;
        goto HaveSku;
    } // else
    // else it is server or advanced server.  I don't think that at this point
    // we can easily tell the difference.  Since it's been said that having the
    // correct sku is not critically important, I shall give them both the sku
    // code of server
    pszSkuCode = szSkuServerEval;
    goto HaveSku;
    }

    // check for NFR
    if (!_wcsicmp(Pid30Rpc,L"51883")){
    pszSkuCode = szSkuServerNFR;
    goto HaveSku;
    } // else
    if (!_wcsicmp(Pid30Rpc,L"51882")){
    pszSkuCode = szSkuAdvServerNFR;
    goto HaveSku;
    } // else

    if (CdType == CDRetail) {
    if (!_wcsicmp(Pid30Rpc,L"51873")){
        pszSkuCode = szSkuProfessionalFPP;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51874")){
        pszSkuCode = szSkuProfessionalCCP;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51876")){
        pszSkuCode = szSkuServerFPP;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51877")){
        pszSkuCode = szSkuServerCCP;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51879")){
        pszSkuCode = szSkuAdvServerFPP;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51880")){
        pszSkuCode = szSkuAdvServerCCP;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51891")){
        pszSkuCode = szSkuDTCFPP;
        goto HaveSku;
    }
    } else if (CdType == CDOem) {
        pszSkuCode = szSkuUnknown;
    } else if (CdType == CDSelect) {
    if (!_wcsicmp(Pid30Rpc,L"51873")){
        pszSkuCode = szSkuProfessionalSelect;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51876")){
        pszSkuCode = szSkuServerSelect;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51879")){
        pszSkuCode = szSkuAdvServerSelect;
        goto HaveSku;
    }
    if (!_wcsicmp(Pid30Rpc,L"51891")){
        pszSkuCode = szSkuDTCSelect;
        goto HaveSku;
    }
    }

    pszSkuCode = szSkuUnknown;

HaveSku:

    *(LPDWORD)DigitalProductId = sizeof(DigitalProductId);
    rc = SetupPIDGenW(
                 tmpPid30String,            // [IN] 25-character Secure CD-Key (gets U-Cased)
                 Pid30Rpc,                  // [IN] 5-character Release Product Code
                 pszSkuCode,         // [IN] Stock Keeping Unit (formatted like 123-12345)
                 (CdType == CDOem),         // [IN] is this an OEM install?
                 ProductId20FromProductId30, // [OUT] PID 2.0, pass in ptr to 24 character array
                 DigitalProductId,          // [OUT] pointer to binary PID3 buffer. First DWORD is the length
                 NULL);                      // [OUT] optional ptr to Compliance Checking flag (can be NULL)


#ifdef PRERELEASE
        SetupDebugPrint2(L"Pidgen returns for PID:%s and MPC:%s\n", tmpPid30String, Pid30Rpc);
#endif
    if (!rc) {
#ifdef PRERELEASE
        SetupDebugPrint1(L"Pidgen returns %d for PID.n", rc);
#endif
        ZeroMemory(Pid30Text[0],5*(MAX_PID30_EDIT+1));
    }
    else
    {
        if (*ProductId20FromProductId30 == L'\0')
        {
            SetupDebugPrint(L"ProductId20FromProductId30 is empty after call into pidgen and pidgen returns OK\n");
        }
        if (*DigitalProductId == 0)
        {
            SetupDebugPrint(L"DigitalProductId is empty after call into pidgen and pidgen returns OK\n");
        }
    }

    return rc;

}

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));
}


INT_PTR
CALLBACK
Pid30CDDlgProc(
    IN HWND   hdlg,
    IN UINT   msg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )

/*++

Routine Description:

        Dialog procedure for the CD Retail Pid dialog.

Arguments:

        hWnd - a handle to the dialog proceedure.

        msg - the message passed from Windows.

        wParam - extra message dependent data.

        lParam - extra message dependent data.


Return Value:

        TRUE if the value was edited.  FALSE if cancelled or if no
        changes were made.

--*/
{
    NMHDR *NotifyParams;
    DWORD i,dwRet;


    switch(msg) {

    case WM_INITDIALOG: {

        if( UiTest ) {
            //
            //  If testing the wizard, make sure that the PidOEM page is
            //  displayed
            //
            CdType = CDRetail;
            DisplayPidDialog = TRUE;
        }


        // 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);
        }

        break;
    }
    case WM_IAMVISIBLE:
        MessageBoxFromMessage(hdlg,MSG_PID_IS_INVALID,NULL,
            IDS_ERROR,MB_OK|MB_ICONSTOP);
        break;
    case WM_SIMULATENEXT:
        // Simulate the next button somehow
        PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT);
        break;

    case WM_NOTIFY:

        NotifyParams = (NMHDR *)lParam;

        switch(NotifyParams->code) {

        case PSN_SETACTIVE:
            TESTHOOK(506);
            BEGIN_SECTION(L"Your (Retail) Product Key Page");
            if(DisplayPidDialog && CdType == CDRetail) {
                // Page becomes active, make page visible.
                SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);

                SetWizardButtons(hdlg,WizPageProductIdCd);
                SendDlgItemMessage(hdlg,IDT_EDIT_PID1,EM_SETSEL,0,-1);
                SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1));
            } else {
                SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1);
                END_SECTION(L"Your (Retail) Product Key Page");
                break;
            }
            if(Unattended) {
                if (UnattendSetActiveDlg(hdlg,IDD_PID_CD))
                {
                    // Page becomes active, make page visible.
                    SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);
                }
            }
            break;

        case PSN_WIZNEXT:
        case PSN_WIZFINISH:

            for (i = 0; i<5; i++) {
                GetDlgItemText(hdlg,IDT_EDIT_PID1+i,Pid30Text[i],MAX_PID30_EDIT+1);
            }

            if (!ValidateAndSetPid30()) {

                // failure
                // Tell user that the Pid is not valid, and
                // don't allow next page to be activated.
                //
                if (Unattended) {
                    UnattendErrorDlg( hdlg, IDD_PID_CD );
                }
                MessageBoxFromMessage(hdlg,MSG_PID_IS_INVALID,NULL,
                        IDS_ERROR,MB_OK|MB_ICONSTOP);

                SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1));
                if(!UiTest) {
                    SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1);
                }
            } else {


                // success
                //
                //  Since the Pid is already built, don't let this dialog
                //  be displayed in the future.
                //
                // DisplayPidDialog = FALSE;

                //
                // Allow next page to be activated.
                //
                dwRet = SetCurrentProductIdInRegistry();
                if (dwRet != NOERROR) {
                    SetuplogError(
                        LogSevError,
                        szMSG_LOG_PID_CANT_WRITE_PID,
                        0,
                        dwRet,NULL,NULL);
                }

                SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0);
            }

            break;

        case PSN_KILLACTIVE:
            WizardKillHelp(hdlg);
            SetWindowLongPtr(hdlg,DWLP_MSGRESULT, FALSE);
            END_SECTION(L"Your (Retail) Product Key Page");
            break;

        case PSN_HELP:
            WizardBringUpHelp(hdlg,WizPageProductIdCd);
            break;

        default:
            break;

        }
        break;

    default:
        return(FALSE);
    }

    return(TRUE);
}


INT_PTR
CALLBACK
Pid30OemDlgProc(
    IN HWND   hdlg,
    IN UINT   msg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
/*++

Routine Description:

        Dialog procedure for the OEM Pid dialog.

Arguments:

        hWnd - a handle to the dialog proceedure.

        msg - the message passed from Windows.

        wParam - extra message dependent data.

        lParam - extra message dependent data.


Return Value:

        TRUE if the value was edited.  FALSE if cancelled or if no
        changes were made.

--*/
{
    NMHDR *NotifyParams;
    DWORD i,dwRet;


    switch(msg) {

    case WM_INITDIALOG: {

        if( UiTest ) {
            //
            //  If testing the wizard, make sure that the PidOEM page is
            //  displayed
            //
            CdType = CDOem;
            DisplayPidDialog = TRUE;
        }

        // 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);
        }

        break;

    }
    case WM_SIMULATENEXT:
        // Simulate the next button somehow
        PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT);
        break;

    case WM_IAMVISIBLE:
        MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP);
        break;
    case WM_NOTIFY:

        NotifyParams = (NMHDR *)lParam;

        switch(NotifyParams->code) {

        case PSN_SETACTIVE:
            TESTHOOK(507);
            BEGIN_SECTION(L"Your (OEM) Product Key Page");
            if(DisplayPidDialog && CdType == CDOem) {
                // Page becomes active, make page visible.
                SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);
                SetWizardButtons(hdlg,WizPageProductIdCd);
                SendDlgItemMessage(hdlg,IDT_EDIT_PID1,EM_SETSEL,0,-1);
                SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1));
            } else {
                SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1);
                END_SECTION(L"Your (OEM) Product Key Page");
                break;
            }
            if(Unattended) {
                if (UnattendSetActiveDlg( hdlg, IDD_PID_OEM ))
                {
                    // Page becomes active, make page visible.
                    SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);
                }
            }
            break;

        case PSN_WIZNEXT:
        case PSN_WIZFINISH:

            for (i = 0; i<5; i++) {
                GetDlgItemText(hdlg,IDT_EDIT_PID1+i,Pid30Text[i],MAX_PID30_EDIT+1);
            }


            if (!ValidateAndSetPid30()) {

                // failure
                //
                // Tell user that the Pid is not valid, and
                // don't allow next page to be activated.
                //
                if (Unattended) {
                    UnattendErrorDlg( hdlg, IDD_PID_OEM );
                } // if
                MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP);
                SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1));
                if(!UiTest) {
                    SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1);
                }
            } else {

                // success
                //
                //  The Pid is valid.
                //


                //
                //
                //  Since the Pid is already built, don't let this dialog
                //  be displayed in the future.
                //
                // DisplayPidDialog = FALSE;

                // Allow next page to be activated.
                //
                dwRet = SetCurrentProductIdInRegistry();
                if (dwRet != NOERROR) {
                    SetuplogError(
                        LogSevError,
                        szMSG_LOG_PID_CANT_WRITE_PID,
                        0,
                        dwRet,NULL,NULL);
                }

                SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0);

            }
            break;

        case PSN_KILLACTIVE:
            WizardKillHelp(hdlg);
            SetWindowLongPtr(hdlg,DWLP_MSGRESULT, FALSE );
            END_SECTION(L"Your (OEM) Product Key Page");
            break;

        case PSN_HELP:
            WizardBringUpHelp(hdlg,WizPageProductIdCd);
            break;

        default:
            break;
        }
        break;

    default:
        return(FALSE);
    }

    return(TRUE);
}

INT_PTR
CALLBACK
Pid30SelectDlgProc(
    IN HWND   hdlg,
    IN UINT   msg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
/*++

Routine Description:

        Dialog procedure for the OEM Pid dialog.

Arguments:

        hWnd - a handle to the dialog proceedure.

        msg - the message passed from Windows.

        wParam - extra message dependent data.

        lParam - extra message dependent data.


Return Value:

        TRUE if the value was edited.  FALSE if cancelled or if no
        changes were made.

--*/
{
    NMHDR *NotifyParams;
    DWORD i,dwRet;

    switch(msg) {

    case WM_INITDIALOG: {

        if( UiTest ) {
            //
            //  If testing the wizard, make sure that the PidOEM page is
            //  displayed
            //
            CdType = CDSelect;
            DisplayPidDialog = TRUE;
        }

        // 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);
        }

        break;

    }
    case WM_SIMULATENEXT:
        // Simulate the next button somehow
        PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT);
        break;

    case WM_IAMVISIBLE:
        MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP);
        break;
    case WM_NOTIFY:

        NotifyParams = (NMHDR *)lParam;

        switch(NotifyParams->code) {

        case PSN_SETACTIVE:
            TESTHOOK(508);
            BEGIN_SECTION(L"Your (Select) Product Key Page");
            if(DisplayPidDialog && CdType == CDSelect) {
                // Page becomes active, make page visible.
                SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);
                SetWizardButtons(hdlg,WizPageProductIdCd);
                SendDlgItemMessage(hdlg,IDT_EDIT_PID1,EM_SETSEL,0,-1);
                SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1));
            } else {
                SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1);
                END_SECTION(L"Your (Select) Product Key Page");
                break;
            }
            if(Unattended) {
                if (UnattendSetActiveDlg( hdlg, IDD_PID_SELECT ))
                {
                    // Page becomes active, make page visible.
                    SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);
                }
            }
            break;

        case PSN_WIZNEXT:
        case PSN_WIZFINISH:

            for (i = 0; i<5; i++) {
                GetDlgItemText(hdlg,IDT_EDIT_PID1+i,Pid30Text[i],MAX_PID30_EDIT+1);
            }


            if (!ValidateAndSetPid30()) {

                // failure
                //
                // Tell user that the Pid is not valid, and
                // don't allow next page to be activated.
                //
                if (Unattended) {
                    UnattendErrorDlg( hdlg, IDD_PID_SELECT );
                } // if
                MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP);
                SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1));
                if(!UiTest) {
                    SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1);
                }
            } else {

                // success
                //
                //  The Pid is valid.
                //


                //
                //
                //  Since the Pid is already built, don't let this dialog
                //  be displayed in the future.
                //
                // DisplayPidDialog = FALSE;


                // Allow next page to be activated.
                //
                dwRet = SetCurrentProductIdInRegistry();
                if (dwRet != NOERROR) {
                    SetuplogError(
                        LogSevError,
                        szMSG_LOG_PID_CANT_WRITE_PID,
                        0,
                        dwRet,NULL,NULL);
                }
                SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0);

            }
            break;

        case PSN_KILLACTIVE:
            WizardKillHelp(hdlg);
            SetWindowLongPtr(hdlg,DWLP_MSGRESULT, FALSE );
            END_SECTION(L"Your (Select) Product Key Page");
            break;

        case PSN_HELP:
            WizardBringUpHelp(hdlg,WizPageProductIdCd);
            break;

        default:
            break;
        }
        break;

    default:
        return(FALSE);
    }

    return(TRUE);
}

BOOL
SetPid30Variables(
    PWSTR   Buffer
    )
{
    LPWSTR ptr;
    UINT i;


    //
    // all install cases are the same for pid3.0
    // 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
    //
    //
    if(   ( wcslen( Buffer ) !=  (4+ MAX_PID30_EDIT*5)) ||
          ( Buffer[5]  != (WCHAR)L'-' ) ||
          ( Buffer[11] != (WCHAR)L'-' ) ||
          ( Buffer[17] != (WCHAR)L'-' ) ||
          ( Buffer[23] != (WCHAR)L'-' )
      ) {
          //
          // The Pid in the unattended script file is invalid.
          //
          return(FALSE);
    }


    for (i = 0;i<5;i++) {
        //
        // quintet i
        //
        ptr = &Buffer[i*(MAX_PID30_EDIT+1)];
        wcsncpy(Pid30Text[i], ptr, MAX_PID30_EDIT+1 );
        Pid30Text[i][MAX_PID30_EDIT] = (WCHAR)L'\0';
    }

    return TRUE;
}


BOOL
SetPid30FromAnswerFile(
    )
/*++

Routine Description:

        set the pid3.0 globals based on unattend file parameter, if it exists.

Arguments:

        None.

Return Value:


--*/

{
    WCHAR Buffer[MAX_BUF];
    DWORD dwRet;



    if (!GetPrivateProfileString(pwUserData,
                                 pwProductKey,
                                 L"",
                                 Buffer,
                                 sizeof(Buffer)/sizeof(WCHAR),
                                 AnswerFile)) {
       return(FALSE);
    }

    if (!Buffer || !*Buffer) {
       return(FALSE);
    }

    // Buffer contains the Product ID 
    // Is the PID encrypted?
    if (lstrlen(Buffer) > (4 + MAX_PID30_EDIT*5))
    {
        LPWSTR szDecryptedPID = NULL;
        if (ValidateEncryptedPID(Buffer, &szDecryptedPID) == S_OK)
        {
            lstrcpyn(Buffer, szDecryptedPID, sizeof(Buffer)/sizeof(WCHAR));
        }
        if (szDecryptedPID)
        {
            GlobalFree(szDecryptedPID);
        }
    }

    if ( !SetPid30Variables( Buffer ) ) {
        return FALSE;
    }

    SetupDebugPrint(L"Found Product key in Answer file.\n");
    //
    // check with pid30 to make sure it's valid
    //
    if (!ValidateAndSetPid30()) {
        return(FALSE);
    }

    dwRet = SetCurrentProductIdInRegistry();
    if (dwRet != NOERROR) {
        SetuplogError(
            LogSevError,
            szMSG_LOG_PID_CANT_WRITE_PID,
            0,
            dwRet,NULL,NULL);
    }
    return(TRUE);

}



BOOL
InitializePid20Array(
    )
/*++

Routine Description:

        Build the array that contains all Pid20 found in the machine
        during textmode setup.  Even though we are using pid30 now, we still have
        a pid20 string id (pid30 is binary and can't be displayed to the user)

Arguments:

        None.

Return Value:


--*/

{
    LONG    Error;
    HKEY    Key;
    DWORD   cbData;
    WCHAR   Data[ MAX_PATH + 1];
    DWORD   Type;
    ULONG   i;
    ULONG   PidIndex;
    ULONG   Values;
    WCHAR   ValueName[ MAX_PATH + 1 ];

    Pid20Array = NULL;
    //
    //  Get the Pid from HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid
    //
    Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                          szPidListKeyName,
                          0,
                          KEY_READ,
                          &Key );

    if( Error != ERROR_SUCCESS ) {
        return( FALSE );
    }

    Error = RegQueryInfoKey( Key,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             &Values,
                             NULL,
                             NULL,
                             NULL,
                             NULL );

    if( Error != ERROR_SUCCESS ) {
        return( FALSE );
    }

    Pid20Array = (PWSTR *)MyMalloc( (Values + 1)* sizeof( PWSTR ) );

    for( i = 0, PidIndex = 0; i < Values; i++ ) {
        Pid20Array[PidIndex] = NULL;
        Pid20Array[PidIndex + 1] = NULL;
        swprintf( ValueName, L"Pid_%u", i );
        cbData = sizeof(Data);
        Error = RegQueryValueEx( Key,
                                 ValueName,
                                 0,
                                 &Type,
                                 ( LPBYTE )Data,
                                 &cbData );
        if( (Error != ERROR_SUCCESS) ||
            ( Type != REG_SZ ) ||
            ( wcslen( Data ) != MAX_PRODUCT_ID ) ) {
            continue;
        }
        Pid20Array[PidIndex] = pSetupDuplicateString( Data );
        PidIndex++;
    }
    RegCloseKey( Key );
    return( TRUE );
}


BOOL
InitializePidVariables(
    )
/*++

Routine Description:

        Read from the registry some values created by textmode setup,
        and initialize some global Pid flags based on the values found

Arguments:

        None.

Return Value:

        Returns TRUE if the initialization succedded.
        Returns FALSE if the Pid could not be read from the registry

--*/

{
    LONG    Error;
    HKEY    Key;
    DWORD   cbData;
    WCHAR   Data[ MAX_PATH + 1];
    DWORD   Type;
    ULONG   StringLength;
    PWSTR   p;
    DWORD   Seed;
    DWORD   RandomNumber;
    ULONG   ChkDigit;
    ULONG   i;
    PCWSTR  q;
    BOOLEAN KeyPresent;
    WCHAR   KeyBuffer[MAX_BUF];


    //
    // find out if product key was entered by the user or not
    // NB : set the answer file (if needed)
    //
    if (!AnswerFile[0])
      SpSetupLoadParameter(pwProductKey, KeyBuffer, sizeof(KeyBuffer)/sizeof(WCHAR));

    KeyBuffer[0] = 0;
    KeyPresent = ((GetPrivateProfileString(pwUserData, pwProductKey,
                      pwNull, KeyBuffer, sizeof(KeyBuffer)/sizeof(WCHAR),
                      AnswerFile) != 0) &&
                  (KeyBuffer[0] != 0));

    //  First create an array with the Pids found during textmode setup
    //
    if( !(MiniSetup || OobeSetup) ) {
        InitializePid20Array();
    }


    //
    //  Get the Pid from HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid
    //
    Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                          ((MiniSetup || OobeSetup) ? szFinalPidKeyName : szPidKeyName),
                          0,
                          KEY_READ,
                          &Key );

    if( Error != ERROR_SUCCESS ) {
        SetuplogError( LogSevFatalError,
            SETUPLOG_USE_MESSAGEID,
            MSG_LOG_PID_CANT_READ_PID, NULL,
            SETUPLOG_USE_MESSAGEID,
            MSG_LOG_X_PARAM_RETURNED_WINERR,
            szRegOpenKeyEx,
            Error,
            szPidKeyName,
            NULL,NULL);
        return( FALSE );
    }

    cbData = sizeof(Data);
    Error = RegQueryValueEx( Key,
                             ((MiniSetup || OobeSetup) ? szFinalPidValueName : szPidValueName),
                             0,
                             &Type,
                             ( LPBYTE )Data,
                             &cbData );
    RegCloseKey( Key );
    if( (Error != ERROR_SUCCESS) ) {
        SetuplogError( LogSevFatalError,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_CANT_READ_PID, NULL,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_X_PARAM_RETURNED_WINERR,
                  szRegQueryValueEx,
                  Error,
                  szPidValueName,
                  NULL,NULL);
        return( FALSE );
    }

    //
    // Take care of the mini-setup case first because it's quick.
    // The Pid seeds left behind by textmode are long gone, so
    // we're going to pull out a few rabbits.  We'll go read the
    // real Pid (the one gui-mode generated the first time he
    // ran through) and use that to determine which kind of
    // PID to prompt for later on.
    //
    if( MiniSetup || OobeSetup ) {

        //
        // tuck away the rpc code for later on
        //
        wcsncpy( Pid30Rpc, Data, MAX_PID30_RPC +1 );
        Pid30Rpc[MAX_PID30_RPC] = (WCHAR)'\0';

        p = Data + (MAX_PID30_RPC + 1);
        wcsncpy(Pid30Site,p,MAX_PID30_SITE+1);
        Pid30Site[MAX_PID30_SITE] = (WCHAR)'\0';
        //
        // Look to see what kind of media we're installing from.
        //
        CdType = MiniSetupGetCdType(Pid30Site);

        if (CdType == CDSelect)
        {
            goto SelectPid;
        }
        else
        {
            DisplayPidDialog = TRUE;
        }

        return( TRUE );
    }



    //
    //  Do some validation of the value read
    //

    if( ( Type != REG_SZ ) ||
        ( ( ( StringLength = wcslen( Data ) ) != 0 ) &&
          ( StringLength != MAX_PID30_RPC ) &&
          ( StringLength != MAX_PID30_RPC + MAX_PID30_SITE )
        )
      ) {
        SetuplogError( LogSevFatalError,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_CANT_READ_PID, NULL,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_INVALID_PID,
                  szRegQueryValueEx,
                  Type,
                  StringLength,
                  NULL,NULL);
        return( FALSE );
    }

    //
    // tuck away the rpc code for later on
    //
    wcsncpy( Pid30Rpc, Data, MAX_PID30_RPC +1 );
    Pid30Rpc[MAX_PID30_RPC] = (WCHAR)'\0';

    //
    //  Find out the kind of product we have (by looking at the site code):
    //  CD Retail, OEM or Select
    //

    if( StringLength > MAX_PID30_RPC ) {
        //
        //  If the Pid contains the Site, then find out what it is
        //
        p = Data + MAX_PID30_RPC;
        wcsncpy(Pid30Site,p,MAX_PID30_SITE+1);

        if(_wcsicmp( Pid30Site, szPidSelectId ) == 0) {

            //
            //  This is a Select CD
            //
SelectPid:
            CdType = CDSelect;
            if (!EulaComplete && !KeyPresent) {
                DisplayPidDialog = TRUE;
            } else {
                //
                //  The Pid was specified during winnt32.
                //  Set the pid globals and build the product id string
                //

                if (!SetPid30FromAnswerFile()) {
                   DisplayPidDialog = TRUE;
                   goto finish;
                }
                DisplayPidDialog = FALSE;

            }
/*
// Old code. previous version of Windows did not require a PID for Select media.
            for (i = 0; i< 5; i++) {
                Pid30Text[i][0] = (WCHAR)L'\0';
            }

            DisplayPidDialog = FALSE;

            if (!ValidateAndSetPid30()) {
                SetuplogError( LogSevFatalError,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_CANT_READ_PID, NULL,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_INVALID_PID,
                  szRegQueryValueEx,
                  Type,
                  StringLength,
                  NULL,NULL);
                return( FALSE );
            }

            if (MiniSetup || OobeSetup) {
                return(TRUE);
            }
*/
#if 0
// msdn media no longer exists (and if it does it should be viewed as retail,
// so later in this case statement we will fall thru to retail
        } else if (_wcsicmp( Pid30Site, szPidMsdnId ) == 0) {

            //
            //  This is an MSDN CD
            //
MsdnPid:
        for (i = 0; i< 5; i++) {
                LPWSTR ptr;
                ptr = (LPTSTR) &szPid30Msdn[i*(MAX_PID30_EDIT+1)];
                wcsncpy(Pid30Text[i], ptr, MAX_PID30_EDIT+1 );
                Pid30Text[i][MAX_PID30_EDIT] = (WCHAR)L'\0';
        }
            CdType = CDSelect;
        DisplayPidDialog = FALSE;
            if (!ValidateAndSetPid30()) {
                SetuplogError( LogSevFatalError,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_CANT_READ_PID, NULL,
                  SETUPLOG_USE_MESSAGEID,
                  MSG_LOG_PID_INVALID_PID,
                  szRegQueryValueEx,
                  Type,
                  StringLength,
                  NULL,NULL);
                return( FALSE );
            }

            if (MiniSetup) {
                return(TRUE);
            }
#endif
        } else if( _wcsicmp( Pid30Site, szPidOemId ) == 0 ) {
            //
            //  This is OEM
            //
            CdType = CDOem;

            if (!EulaComplete && !KeyPresent) {
                DisplayPidDialog = TRUE;
            } else {
                //
                //  The Pid was specified during winnt32.
                //  Set the pid globals and build the product id string
                //
                if (!SetPid30FromAnswerFile() ) {
                   DisplayPidDialog = TRUE;
                   goto finish;
                }

                DisplayPidDialog = FALSE;
            }

        } else {
            //
            // This is a bogus site assume CD Retail
            //
            CdType = CDRetail;
            wcsncpy( Pid30Site, L"000", MAX_PID30_SITE+1 );
            Pid30Site[ MAX_PID30_SITE ] = (WCHAR)'\0';

            if (!EulaComplete && !KeyPresent) {
                DisplayPidDialog = TRUE;
            } else {
                //
                //  The Pid was specified during winnt32.
                //  Set the pid globals and build the product id string
                //

                if (!SetPid30FromAnswerFile()) {
                   DisplayPidDialog = TRUE;
                   goto finish;
                }
                DisplayPidDialog = FALSE;

            }

        }


    } else {
        //
        //  If it doesn't contain the Site, then it is a CD retail,
        //  and the appropriate Pid dialog must be displayed.
        //
        CdType = CDRetail;
        wcsncpy( Pid30Site, L"000", MAX_PID30_SITE+1 );
        Pid30Site[ MAX_PID30_SITE ] = (WCHAR)'\0';

        if (!EulaComplete && !KeyPresent) {
            DisplayPidDialog = TRUE;
        } else {
            //
            //  The Pid was specified during winnt32.
            //  Set the pid globals and build the product id string
            //
            if (!SetPid30FromAnswerFile()) {
               DisplayPidDialog = TRUE;
               goto finish;
            }
            DisplayPidDialog = FALSE;
        }
    }

finish:
    //
    //  Don't remove the Setup\Pid here. See MiniSetupGetCdType
    //  Delete Setup\PidList since it is no longer needed
    //
    Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                          L"SYSTEM\\Setup",
                          0,
                          MAXIMUM_ALLOWED,
                          &Key );

    if( Error == ERROR_SUCCESS ) {
        // pSetupRegistryDelnode( Key, L"Pid" );
        pSetupRegistryDelnode( Key, L"PidList" );
        RegCloseKey( Key );
    }

    return( TRUE );
}