// This is the migration dll for NT5 upgrades.
// As per the Migration Extension Interface of NT5 Setup, this DLL needs to
// implement the folowing six functions:
//        QueryVersion
//        Initialize9x
//         MigrateUser9x     (called once for every user)
//        MigrateSystem9x
//        InitializeNT
//        MigrateUserNT     (called once for every user)
//        MigrateSystemNT
//
// Written : ShabbirS (5/7/99)
// Revision:
//


#include "pch.h"
#include <ole2.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <excppkg.h>
#ifdef __cplusplus
}
#endif  /* __cplusplus */

#include "sdsutils.h"
#include "advpub.h"
#include "migrate.h"
#include "utils.h"
#include "resource.h"

// Constants:
#define CP_USASCII            1252
#define END_OF_CODEPAGES    -1

// Globals that are passed back to Setup.
//////////////////////////////////////////
// Vendor Info:
VENDORINFO g_VendorInfo = { "Microsoft Corporation", 
                            " ", 
                            "http://www.microsoft.com/support",
                            "Please contact Microsoft Technical Support for assistance with this problem.  "};

// Product ID:
char g_cszProductID[] = "Microsoft Internet Explorer";

// Version number of this Migration Dll
UINT g_uVersion = 3;

// Array of integers specifying the CodePages we use. (Terminated with -1)
int  g_rCodePages[] = {CP_USASCII, END_OF_CODEPAGES};

// Multi-SZ ie double Null terminated list of strings.
char  *g_lpNameBuf = NULL;
DWORD  g_dwNameBufSize = 0;
char  *g_lpWorkingDir = NULL;
char  *g_lpSourceDirs = NULL;
char  *g_lpMediaDir = NULL;

char g_szMigrateInf[MAX_PATH];
char g_szPrivateInf[MAX_PATH];

LONG
CALLBACK
QueryVersion(   OUT LPCSTR *ProductID,
                OUT LPUINT DllVersion,
                OUT LPINT  *CodePageArray, OPTIONAL
                OUT LPCSTR *ExeNameBuf,    OPTIONAL
                OUT PVENDORINFO *VendorInfo
            )
{
    // NOTE: There is timing restriction on the return from this function
    //       So keep this as short and sweet as possible.
    VENDORINFO myVendorInfo;
    LONG lRet = ERROR_SUCCESS;

    AppendString(&g_lpNameBuf, &g_dwNameBufSize, cszRATINGSFILE);
    AppendString(&g_lpNameBuf, &g_dwNameBufSize, cszIEXPLOREFILE);

    // Pass back to Setup the product name.
    *ProductID = g_cszProductID;

    // Pass back to Setup the version number of this DLL.
    *DllVersion = g_uVersion;

    // We will use English messages only but don't specify a code page or 
    // the migration dll won't run on alternate code pages.
    *CodePageArray = NULL;

    // Pass back to Setup the list of files we want detected on this system.
    *ExeNameBuf = g_lpNameBuf;

    // Pass back the VendorInfo.
    if (LoadString(g_hInstance, IDS_COMPANY, myVendorInfo.CompanyName, sizeof(myVendorInfo.CompanyName)) == 0)
        lstrcpy(myVendorInfo.CompanyName, "Microsoft Corporation");

    if (LoadString(g_hInstance, IDS_SUPPORTNUMBER, myVendorInfo.SupportNumber, sizeof(myVendorInfo.SupportNumber)) == 0)
        lstrcpy(myVendorInfo.SupportNumber, " ");

    if (LoadString(g_hInstance, IDS_SUPPORTURL, myVendorInfo.SupportUrl, sizeof(myVendorInfo.SupportUrl)) == 0)
        lstrcpy(myVendorInfo.SupportUrl, "http://www.microsoft.com/support");

    if (LoadString(g_hInstance, IDS_INSTRUCTIONS, myVendorInfo.InstructionsToUser, sizeof(myVendorInfo.InstructionsToUser)) == 0)
        lstrcpy(myVendorInfo.InstructionsToUser, "Please contact Microsoft Technical Support for assistance with this problem.  ");

    *VendorInfo = &myVendorInfo;

#ifdef DEBUG
    char szDebugMsg[MAX_PATH*3];
    wsprintf(szDebugMsg,"IE6:ProductID: %s \r\n", *ProductID);
    SetupLogError(szDebugMsg, LogSevInformation);
#endif

    return lRet;

}

LONG
CALLBACK
Initialize9x(   IN    LPCSTR WorkingDir,
                IN    LPCSTR SourceDirs,
                IN    LPCSTR MediaDirs
            )
{
    // Called by NT Setup if QUeryVersion returned SUCCESS
    // At this point we have been relocated to some specific location
    // on the local drive by the Setup process.

    INT    len;

    // Keep track of our new location (ie. the Working Directory).
    // NT Setup will create the "MIGRATE.INF" file in this dir and use that
    // to exchange info with us.
    // Also we can use this Dir for saving our private stuff. NT Setup will 
    // ensure this folder stays till end of NT Migration. After that it will
    // be cleaned.
    len = lstrlen(WorkingDir) + 1;
    g_lpWorkingDir = (char *) LocalAlloc(LPTR,sizeof(char)*len);

    if (!g_lpWorkingDir)
    {
        return GetLastError();
    }

    CopyMemory(g_lpWorkingDir, WorkingDir, len);


    len = lstrlen(MediaDirs) + 1;
    g_lpMediaDir = (char *) LocalAlloc(LPTR,sizeof(char)*len);

    if (!g_lpMediaDir)
    {
        return GetLastError();
    }

    CopyMemory(g_lpMediaDir, MediaDirs, len);

    // Also keep track of the NT installation files path (ie Sources Dir).
    // NOTE: Right now we don't need it, so skip doing it.

    // Generate the path names to Migrate.inf and the private files
    // (private.inf) that we need
    GenerateFilePaths();
    
    // If NT Setup has succeeded in getting path to Ratings.Pol, it implies
    // Ratings information exists. Enable our private marker to do the right
    // thing in MigrateSystemNT phase.
    if (GetRatingsPathFromMigInf(NULL))
    {    // Put a marker in PRIVATE.INF so that MigrateSystemNT phase knows 
        // that it has to munge the Rating.

        // Private.Inf does not exist at this point so why this check!!!
        // if (GetFileAttributes(g_szPrivateInf) != 0xffffffff)
        WritePrivateProfileString(cszIEPRIVATE, cszRATINGS, "Yes", g_szPrivateInf);
        // Flush the cached entries to disk.
        WritePrivateProfileString(NULL,NULL,NULL,g_szPrivateInf);

#ifdef DEBUG
    SetupLogError("IE6: Created PRIVATE.INF\r\n", LogSevInformation);
#endif
    }

    return ERROR_SUCCESS;
}


LONG
CALLBACK
MigrateUser9x(  IN HWND      ParentWnd,
                IN LPCSTR    UnattendFile,
                IN HKEY      UserRegKey,
                IN LPCSTR    UserName,
                   LPVOID    Reserved
            )
{
    // This function is called by NT Setup for each user.
    // Currently for the Ratings scenario, we don't need any PerUser action.
#ifdef DEBUG
    SetupLogError("IE6: Skipping MigrateUser9x \r\n", LogSevInformation);
#endif
    return ERROR_SUCCESS;

}


LONG
CALLBACK
MigrateSystem9x(    IN HWND      ParentWnd,
                    IN LPCSTR    AnswerFile,
                    LPVOID       Reserved
                )
{
    // This function is called once by NT Setup to let us save System wide info.
    //
    // we are writing the "incompatibility report here if IE5.5 is installed and 
    // the user does not have the full migration pack installed.
    //

    char    szCab[MAX_PATH];
    WORD  wVer[4];
    char szBuf[MAX_PATH];

    // Check if we have the full exception pack which re-installs IE5.5

    // NOTE: g_lpMediaDir is the location where the migration dll was installed/registered
    // This is the same location where the INF is.
    lstrcpy(szCab, g_lpMediaDir);
    AddPath(szCab, "ieexinst.inf");
    if (GetPrivateProfileString("Info", "Version", "", szBuf , MAX_PATH, szCab) != 0) 
    {
        // Convert version
        ConvertVersionString( szBuf, wVer, '.' );

        if ((wVer[0] == 5) && (wVer[1] == 50))
        {
            // We don't have the full exception pack.
            // generate the "incompatibility report"
            // g_szMigrateInf
            lstrcpy(szBuf, g_lpMediaDir);
            GetParentDir(szBuf);

            if (LoadString(g_hInstance, IDS_INCOMPAT_MSG, szCab, sizeof(szCab)))
            {
                WritePrivateProfileString(cszMIGINF_INCOMPAT_MSG, g_cszProductID, szCab, g_szMigrateInf);
                WritePrivateProfileString(g_cszProductID, szBuf, "Report", g_szMigrateInf);
                WritePrivateProfileString(NULL,NULL,NULL,g_szMigrateInf);
            }
        }
    }

    WritePrivateProfileString(cszMIGINF_HANDLED, "HKLM\\Software\\Microsoft\\Active Setup\\ClsidFeature", "Registry", g_szMigrateInf);
    WritePrivateProfileString(cszMIGINF_HANDLED, "HKLM\\Software\\Microsoft\\Active Setup\\FeatureComponentID", "Registry", g_szMigrateInf);
    WritePrivateProfileString(cszMIGINF_HANDLED, "HKLM\\Software\\Microsoft\\Active Setup\\MimeFeature", "Registry", g_szMigrateInf);
    WritePrivateProfileString(NULL,NULL,NULL,g_szMigrateInf);
#ifdef DEBUG
    SetupLogError("IE6: MigrateSystem9x \r\n", LogSevInformation);
#endif
    
    return ERROR_SUCCESS;
}



LONG
CALLBACK 
InitializeNT (
    IN      LPCWSTR WorkingDirectory,
    IN      LPCWSTR SourceDirectories,
            LPVOID  Reserved
    )
{
    INT Length;
    LPCWSTR p;

    //
    // Save our working directory and source directory.  We
    // convert UNICODE to ANSI, and we use the system code page.
    //

    //
    // Compute length of source directories
    //

    p = SourceDirectories;
    while (*p) {
        p = wcschr (p, 0) + 1;
    }
    p++;
    Length = (p - SourceDirectories) / sizeof (WCHAR);

    //
    // Convert the directories from UNICODE to DBCS.  This DLL is
    // compiled in ANSI.
    //

    g_lpWorkingDir = (LPSTR) LocalAlloc(LPTR, MAX_PATH);
    if (!g_lpWorkingDir) {
        return GetLastError();
    }

    WideCharToMultiByte (
        CP_ACP, 
        0, 
        WorkingDirectory, 
        -1,
        g_lpWorkingDir,
        MAX_PATH,
        NULL,
        NULL
        );

    //  Also save the SourceDirectories that points to the Windows
    //  NT media (i.e. e:\i386) and optional directories specified on
    //  the WINNT32 command line. Not used currently, so skip.

    // Now generate the derived file names
    GenerateFilePaths();

#ifdef DEBUG
    SetupLogError("IE6: Done InitializeNT \r\n", LogSevInformation);
#endif

    return ERROR_SUCCESS;
}

LONG
CALLBACK 
MigrateUserNT (
    IN      HINF UnattendInfHandle,
    IN      HKEY UserRegKey,
    IN      LPCWSTR UserName,
            LPVOID Reserved
    )
{
    // No per-user settings for Ratings upgrade.
#ifdef DEBUG
    SetupLogError("IE6: Skipping MigrateUserNT \r\n", LogSevInformation);
#endif
    
    return ERROR_SUCCESS;
}


#define PACKAGE_GUID       "{89820200-ECBD-11cf-8B85-00AA005B4383}"
#define PACKAGE_DIRECTORY    "%windir%\\RegisteredPackages\\"


LONG
CALLBACK 
MigrateSystemNT (
    IN      HINF UnattendInfHandle,
            LPVOID Reserved
    )
{    // NOTE: This phase MUST finish in 60 seconds or will be terminated.
    
    // Check if our PRIVATE.INF exists and perform relevant actions based on its contents.
    CHAR szBuffer[3+10];

    if (GetFileAttributes(g_szPrivateInf) != 0xffffffff)
    {
        GetPrivateProfileString(cszIEPRIVATE, cszRATINGS, "", szBuffer, sizeof(szBuffer), g_szPrivateInf);
        if (lstrcmpi(szBuffer,"Yes")==0)
        {
            UpgradeRatings();
            SetupLogError("IE Migration: Upgraded Ratings info.\r\n", LogSevInformation);
        }
    }
    else
    {
        SetupLogError("IE Migration: No Rating migration. Private.Inf does not exist.\r\n",LogSevInformation);
    }

#if 0
    // Do the W2K IE5.5 migration work here.
    // 1. Copy all files from the IE location to the registered migration pack location
    // 2. Register the migration pack
    SETUP_OS_COMPONENT_DATA ComponentData,cd;
    SETUP_OS_EXCEPTION_DATA ExceptionData,ed;
    GUID MyGuid;
    PWSTR GuidString;
    PSTR  t;
    BOOL  bContinue = FALSE;
    WCHAR szMsg[1024];
    char  szPath[MAX_PATH];
    LPWSTR pszwPath;
    WORD  wVer[4];
    char szBuf[MAX_PATH];
    char szInf[MAX_PATH];
    char szGUID[MAX_PATH];
    char szCab[MAX_PATH];
    char szDir[MAX_PATH];
#ifdef DEBUG
    char sz[1024];
#endif
    HRESULT hr;

    char szCabs[1024];
    LPSTR pCab = NULL;

    // Get the INF which is installed in the W2K folder.
    // This INF tells us the info about the IE exception pack.
    // g_lpWorkingDir contains all fiels and sub folders which are in the same place 
    // as the registerd migration dll. Since we install the files in the same folder, we can use it.
    lstrcpy(szInf, g_lpWorkingDir);
    AddPath(szInf, "ieexinst.inf");
#ifdef DEBUG
    wsprintf(sz, "IE exception INF :%s:\r\n", szInf);
    SetupLogError(sz,LogSevInformation);
#endif
    if (GetFileAttributes(szInf) != (DWORD)-1)
    {
        // Get the GUID
        if (GetPrivateProfileString("Info", "ComponentId", "", szGUID, sizeof(szGUID), szInf) == 0)
            lstrcpy(szGUID, PACKAGE_GUID);
        
        ExpandEnvironmentStrings( PACKAGE_DIRECTORY, szDir, sizeof(szDir));
        if (GetFileAttributes(szDir) == (DWORD)-1)
            CreateDirectory( szDir, NULL );
        AddPath(szDir, szGUID);
        if (GetFileAttributes(szDir) == (DWORD)-1)
            CreateDirectory( szDir, NULL );
        
        // BUGBUG:
        // The extraction of the CAB file(s) should be done after we found out
        // If the user has already a newer exception pack registered.
        // This check is done below. Could not change it anymore, because of
        // time constrains. Found this in the code review
        //
        // Extract all CABs into the package fodler.
#ifdef DEBUG
        wsprintf(sz, "cab folder :%s:\r\n", g_lpWorkingDir);
        SetupLogError(sz,LogSevInformation);
        wsprintf(sz, "extract folder :%s:\r\n", szDir);
        SetupLogError(sz,LogSevInformation);
#endif
        if (GetPrivateProfileSection("Cab.List", szCabs, sizeof(szCabs), szInf) != 0)
        {
            pCab = szCabs;
            while (*pCab != '\0')
            {
                lstrcpy(szCab, g_lpWorkingDir);
                AddPath(szCab, pCab);
#ifdef DEBUG
                wsprintf(sz, "Extract :%s: to :%s:\r\n", szCab, szDir);
                SetupLogError(sz,LogSevInformation);
#endif
                
                hr = ExtractFiles(szCab, szDir, 0, NULL, NULL, 0);
                pCab += (lstrlen(pCab) + 1);
            }
            bContinue = TRUE;
        }
    }

    if (bContinue)
    {
        if (GetPrivateProfileString("Info", "Version", "", szBuf , MAX_PATH, szInf) != 0) 
        {
            // Convert version
            ConvertVersionString( szBuf, wVer, '.' );

            ComponentData.SizeOfStruct = sizeof(SETUP_OS_COMPONENT_DATA);
            ExceptionData.SizeOfStruct = sizeof(SETUP_OS_EXCEPTION_DATA);
            pszwPath = MakeWideStrFromAnsi(szGUID);
            if (pszwPath)
            {
                IIDFromString( pszwPath, &MyGuid);
                CoTaskMemFree(pszwPath);

                if (SetupQueryRegisteredOsComponent(
                                            &MyGuid,
                                            &ComponentData,
                                            &ExceptionData)) 
                {
                    if ((ComponentData.VersionMajor < wVer[0]) ||
                        ((ComponentData.VersionMajor == wVer[0]) && (ComponentData.VersionMinor <= wVer[1])) )
                    {
                        bContinue = SetupUnRegisterOsComponent(&MyGuid);
                        SetupLogError("IE6: SetupUnRegisterOsComponent.\r\n",LogSevInformation);
                    }
                    // BUGBUG: Missing the below else. Found in code review
                    // else
                    //     bContinue = FALSE;
                }
            }
            else
                bContinue = FALSE;
        }
        else
            bContinue = FALSE;
    }
    if (bContinue)
    {
        SetupLogError("IE6: Preparing SetupRegisterOsComponent.\r\n",LogSevInformation);
        ExpandEnvironmentStrings( PACKAGE_DIRECTORY, szPath, sizeof(szPath));
        AddPath( szPath, szGUID );

        ComponentData.VersionMajor = wVer[0];
        ComponentData.VersionMinor = wVer[1];
        RtlMoveMemory(&ComponentData.ComponentGuid, &MyGuid,sizeof(GUID));

        t = szPath + lstrlen(szPath);
        *t = '\0';
        GetPrivateProfileString("Info", "InfFile", "", szBuf, MAX_PATH, szInf);
        AddPath( szPath, szBuf);

        pszwPath = MakeWideStrFromAnsi(szPath);
        if (pszwPath)
        {
            wcscpy(ExceptionData.ExceptionInfName, pszwPath);
            CoTaskMemFree(pszwPath);
        }

        *t = '\0';
        GetPrivateProfileString("Info", "CatalogFile", "", szBuf, MAX_PATH, szInf);
        AddPath( szPath, szBuf);

        pszwPath = MakeWideStrFromAnsi(szPath);
        if (pszwPath)
        {
            wcscpy(ExceptionData.CatalogFileName, pszwPath);
            CoTaskMemFree(pszwPath);
        }

        LoadString(g_hInstance, IDS_FRIENDLYNAME, szPath, sizeof(szPath));
        pszwPath = MakeWideStrFromAnsi(szPath);
        if (pszwPath)
        {
            wcscpy(ComponentData.FriendlyName, pszwPath);
            CoTaskMemFree(pszwPath);
        }

        wsprintfW(szMsg, L"IE6: ExceptionData\r\n\tInf: %ws\r\n\tCatalog: %ws\r\n",
                 ExceptionData.ExceptionInfName,ExceptionData.CatalogFileName);
        SetupLogErrorW(szMsg,LogSevInformation);

        if (SetupRegisterOsComponent(&ComponentData, &ExceptionData)) 
        {
            SetupLogError("IE6: SetupRegisterOsComponent succeeded.\r\n",LogSevInformation);
#ifdef DEBUG
            cd.SizeOfStruct = sizeof(SETUP_OS_COMPONENT_DATA);
            ed.SizeOfStruct = sizeof(SETUP_OS_EXCEPTION_DATA);
            if (SetupQueryRegisteredOsComponent( &MyGuid, &cd, &ed)) 
            {
                StringFromIID(cd.ComponentGuid, &GuidString);
                wsprintfW(szMsg, L"IE6: Component Data\r\n\tName: %ws\r\n\tGuid: %ws\r\n\tVersionMajor: %d\r\n\tVersionMinor: %d\r\n",
                         cd.FriendlyName,GuidString,cd.VersionMajor,cd.VersionMinor);
                SetupLogErrorW(szMsg,LogSevInformation);

                wsprintfW(szMsg, L"IE6: ExceptionData\r\n\tInf: %ws\r\n\tCatalog: %ws\r\n",
                         ed.ExceptionInfName,ed.CatalogFileName);
                SetupLogErrorW(szMsg,LogSevInformation);

                CoTaskMemFree( GuidString );
            }
#endif
        }
    }
    // In future, check for other settings here and perform necessary upgrade actions.
#endif
#ifdef DEBUG
    SetupLogError("IE6: Done MigrateSystemNT \r\n", LogSevInformation);
#endif
    return ERROR_SUCCESS;
}