#include "setupp.h"
#pragma hdrstop

#if 0 // This function appears to never be used.
BOOL
EnqueueFileCopies(
    IN HINF     hInf,
    IN HSPFILEQ FileQ,
    IN PCWSTR   Section,
    IN PCWSTR   TargetRoot
    )
{
    INFCONTEXT InfContext;
    BOOL LineExists;
    WCHAR System32Dir[MAX_PATH];
    PCWSTR SourceFilename,TargetFilename;
    BOOL b;

    GetSystemDirectory(System32Dir,MAX_PATH);
    LineExists = SetupFindFirstLine(hInf,Section,NULL,&InfContext);
    while(LineExists) {

        //
        // Fetch source and target filenames.
        //
        TargetFilename = pSetupGetField(&InfContext,1);
        if(!TargetFilename) {
            return(FALSE);
        }

        SourceFilename = pSetupGetField(&InfContext,2);
        if(!SourceFilename) {
            SourceFilename = TargetFilename;
        }

        //
        // Enqueue the file for copy.
        //
        b = SetupQueueCopy(
                FileQ,
                System32Dir,
                NULL,
                SourceFilename,
                NULL,
                NULL,
                TargetRoot,
                TargetFilename,
                BaseCopyStyle
                );

        if(!b) {
            return(FALSE);
        }
        LineExists = SetupFindNextLine(&InfContext,&InfContext);
    }

    return(TRUE);
}
#endif

BOOL
SideBySidePopulateCopyQueue(
    SIDE_BY_SIDE*     Sxs,
    HSPFILEQ          FileQ,                    OPTIONAL
    PCWSTR            AssembliesRootSource      OPTIONAL
    )
{
    BOOL                    Success = FALSE;
    UINT                    SourceId = 0;
    WCHAR                   DiskNameBuffer[MAX_PATH];
    WCHAR                   PromptForSetupPath[MAX_PATH];
    WCHAR                   AssembliesRootDirectoryFound[MAX_PATH];
    DWORD                   cchAssembliesRootDirectoryFound = sizeof(AssembliesRootDirectoryFound);
    DWORD                   Err;
    WCHAR                   AssembliesRootDirectory[MAX_PATH];
    PCWSTR                  InfField = NULL;
    INFCONTEXT              InfContext = {0};
    BOOL                    LineExists = FALSE;
    SXS_INSTALLW            InstallData;
    SXS_INSTALL_REFERENCEW  InstallReference;
    ASSERT(Sxs != NULL);

    //
    // we depend on these having been initialized, and we are not supposed to
    // be called in MiniSetup or OobeSetup
    //
    ASSERT(SourcePath[0] != 0);
    ASSERT(SyssetupInf != NULL);
    ASSERT(SyssetupInf != INVALID_HANDLE_VALUE);
    ASSERT(!MiniSetup);
    ASSERT(!OobeSetup);

    //
    // first, don't fail to give safe values, since we always try to cleanup
    //
    Sxs->Dll = NULL;
    Sxs->BeginAssemblyInstall = NULL;
    Sxs->EndAssemblyInstall = NULL;
    Sxs->InstallW = NULL;
    Sxs->Context = NULL;

    //
    // then commence with initialization that can fail
    //
    if (!(Sxs->Dll = LoadLibraryW(SXS_DLL_NAME_W))) {
        goto Exit;
    }
    if (!(Sxs->BeginAssemblyInstall = (PSXS_BEGIN_ASSEMBLY_INSTALL)GetProcAddress(Sxs->Dll, SXS_BEGIN_ASSEMBLY_INSTALL))) {
        goto Exit;
    }
    if (!(Sxs->EndAssemblyInstall = (PSXS_END_ASSEMBLY_INSTALL)GetProcAddress(Sxs->Dll, SXS_END_ASSEMBLY_INSTALL))) {
        goto Exit;
    }
    if (!(Sxs->InstallW = (PSXS_INSTALL_W)GetProcAddress(Sxs->Dll, SXS_INSTALL_W))) {
        goto Exit;
    }

    if (!Sxs->BeginAssemblyInstall(
        SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NOT_TRANSACTIONAL
        | SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NO_VERIFY
        | SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_REPLACE_EXISTING,
        (FileQ != NULL) ? SXS_INSTALLATION_FILE_COPY_CALLBACK_SETUP_COPY_QUEUE : NULL,
        FileQ, // callback context
        NULL, // impersonation callback
        NULL, // impersonation context
        &Sxs->Context
        )) {
        goto Exit;
    }

    //
    // Set up the reference data to indicate that all of these are OS-installed
    // assemblies.
    //
    ZeroMemory(&InstallReference, sizeof(InstallReference));
    InstallReference.cbSize = sizeof(InstallReference);
    InstallReference.dwFlags = 0;
    InstallReference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL;

    //
    // Let's get the source disk name of this assembly - we'll need it to
    // pass around as the prompt.
    //
    if ( !SetupGetSourceFileLocation(
        SyssetupInf,
        NULL,
        L"shell32.dll",
        &SourceId,
        NULL,
        0,
        NULL
    ) )
        goto Exit;

    if ( !SetupGetSourceInfo(
        SyssetupInf,
        SourceId,
        SRCINFO_DESCRIPTION,
        DiskNameBuffer,
        sizeof(DiskNameBuffer),
        NULL
    ) )
        goto Exit;


    if (AssembliesRootSource) {

        //
        // Set up the structure to call off to the installer
        //
        memset(&InstallData, 0, sizeof(InstallData));
        InstallData.cbSize = sizeof(InstallData);
        InstallData.dwFlags = SXS_INSTALL_FLAG_FROM_DIRECTORY | 
            SXS_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE | 
            SXS_INSTALL_FLAG_REFERENCE_VALID | 
            SXS_INSTALL_FLAG_REFRESH_PROMPT_VALID |
            SXS_INSTALL_FLAG_INSTALL_COOKIE_VALID |
            SXS_INSTALL_FLAG_INSTALLED_BY_OSSETUP |
            SXS_INSTALL_FLAG_CODEBASE_URL_VALID;
            
        InstallData.lpReference = &InstallReference;
        InstallData.lpRefreshPrompt = DiskNameBuffer;
        InstallData.pvInstallCookie = Sxs->Context;
        InstallData.lpCodebaseURL = AssembliesRootSource;
        InstallData.lpManifestPath = AssembliesRootSource;

        if (!Sxs->InstallW(&InstallData)) {
            // abort call will be made in SideBySideFinish
            goto Exit;
        }
        
    } else {
        LineExists = SetupFindFirstLine(SyssetupInf, SXS_INF_ASSEMBLY_DIRECTORIES_SECTION_NAME_W, NULL, &InfContext);
        while(LineExists) {
            DWORD  FileAttributes = 0;
            //
            // convention introduced specifically for side by side, so that
            // x86 files on ia64 might come from \i386\asms instead of \ia64\asms\i386,
            // depending on what dosnet.inf and syssetup.inf say:
            //   a path that does not start with a slash is appended to \$win_nt$.~ls\processor;
            //   a path that does     start with a slash is appended to \$win_nt$.~ls
            //
            InfField = pSetupGetField(&InfContext, 0);
            if(InfField == NULL) {
                break;
            }

            // c:\$win_nt$.~ls
            lstrcpyn(AssembliesRootDirectory, SourcePath, MAX_PATH);
            if (InfField[0] == '\\' || InfField[0] == '/') {
                InfField += 1;
            } else {
                 // c:\$win_nt$.~ls\i386
                if (!pSetupConcatenatePaths(AssembliesRootDirectory, PlatformName, MAX_PATH, NULL)) {
                    goto Exit;
                }
            }

            // stash this away for a little bit
            lstrcpyn( PromptForSetupPath, AssembliesRootDirectory, MAX_PATH );

            //
            // For now, while "staging", we allow the directory to not exist, and to be
            // empty (emptiness is silently handled elsewhere by common code), but
            // comctl32 will be in an assembly, so assemblies will be mandatory
            // for the system to boot to Explorer.exe.
            //
            // 11/09/2000 (jonwis) If we can't find the assemblies root directory, prompt
            //      for the installation media.  This is ripped straight from the headlines
            //      of crypto.c and cmdline.c.
            //
            for (;;) {

                Err = SetupPromptForDisk(
                    MainWindowHandle,           // Main window handle
                    NULL,                       // Dialog title (defaulted)
                    DiskNameBuffer,             // Name of the disk to request
                    PromptForSetupPath,         // Full path of the asms root
                    InfField,                   // We look to see if the dir is there
                    NULL,                       // No tag file
                    IDF_CHECKFIRST | IDF_NOSKIP | IDF_NODETAILS | IDF_NOBROWSE,
                    AssembliesRootDirectoryFound,       // What we'll use to install
                    cchAssembliesRootDirectoryFound,    // How long is that buffer?
                    NULL
                );

                // See if what we got back from the prompt is success - if so, is the directory
                // really there? We might assume that it is if we get back _SUCCESS...
                if ( Err == DPROMPT_SUCCESS ) {
                    FileAttributes = GetFileAttributes(AssembliesRootDirectoryFound);
                    if ((FileAttributes != 0xFFFFFFFF) && (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
                        // copy out the asms directory location that was found, and
                        // stop looking.
                        lstrcpyn(AssembliesRootDirectory, AssembliesRootDirectoryFound, MAX_PATH);
                        break;
                    }
                } else {
                    break;
                }

            };

            // c:\$win_nt$.~ls\i386\asms
            if (!pSetupConcatenatePaths(AssembliesRootDirectory, InfField, MAX_PATH, NULL)) {
                goto Exit;
            }

            //
            // If we didn't get a success (ie: we broke out of the loop), fail the
            // installation.  Heinous, but MarianT (setup dev) suggests this is the
            // best method.
            //
            if ( Err != DPROMPT_SUCCESS )
                goto Exit;

            //
            // Set up this structure to call off into SXS to do the installation 
            // for us.
            //
            ZeroMemory(&InstallData, sizeof(InstallData));
            InstallData.cbSize = sizeof(InstallData);
            InstallData.dwFlags = SXS_INSTALL_FLAG_FROM_DIRECTORY | 
                SXS_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE | 
                SXS_INSTALL_FLAG_REFERENCE_VALID | 
                SXS_INSTALL_FLAG_REFRESH_PROMPT_VALID |
                SXS_INSTALL_FLAG_INSTALL_COOKIE_VALID |
                SXS_INSTALL_FLAG_INSTALLED_BY_OSSETUP |
                SXS_INSTALL_FLAG_CODEBASE_URL_VALID;

            InstallData.lpManifestPath = AssembliesRootDirectory;
            InstallData.lpReference = &InstallReference;
            InstallData.lpRefreshPrompt = DiskNameBuffer;
            InstallData.pvInstallCookie = Sxs->Context;
            InstallData.lpCodebaseURL = SourcePath;
            
            if (!Sxs->InstallW( &InstallData )) {
                // abort call will be made in SideBySideFinish
                goto Exit;
            }

            LineExists = SetupFindNextLine(&InfContext, &InfContext);
        }
    }

    Success = TRUE;
Exit:
    return Success;
}

BOOL
SideBySideFinish(
    SIDE_BY_SIDE*     Sxs,
    BOOL              fSuccess
    )
{
#define FUNCTION L"SideBySideFinish"
    DWORD dwLastError = NO_ERROR;
    ASSERT(Sxs != NULL);
    //
    // failure to load the .dll or get entry points implies lack of success
    //
    ASSERT(Sxs->Dll != NULL || !fSuccess);
    ASSERT(Sxs->EndAssemblyInstall != NULL || !fSuccess);

    if (!fSuccess) {
        dwLastError = GetLastError();
    }
    if (Sxs->Context != NULL) {
        if (Sxs->EndAssemblyInstall != NULL) {
            if (!Sxs->EndAssemblyInstall(
                    Sxs->Context,
                    fSuccess ? SXS_END_ASSEMBLY_INSTALL_FLAG_COMMIT : SXS_END_ASSEMBLY_INSTALL_FLAG_ABORT,
                    NULL // reserved out DWORD
                    )) {
                if (fSuccess) {
                    fSuccess = FALSE;
                    dwLastError = GetLastError();
                }
            }
        }
        Sxs->Context = NULL;
    }
    if (Sxs->Dll != NULL) {
        if (!FreeLibrary(Sxs->Dll)) {
            if (fSuccess) {
                fSuccess = FALSE;
                dwLastError = GetLastError();
            }
        }
        Sxs->Dll = NULL;
    }

    if (!fSuccess) {
        SetLastError(dwLastError);
    }

    return fSuccess;
#undef FUNCTION    
}


BOOL
SideBySideCreateSyssetupContext(
    VOID
    )
{
#define FUNCTION L"SideBySideCreateSyssetupContext"

    BOOL fSuccess = FALSE;

    const PPEB Peb = NtCurrentPeb();
    ACTCTXW CreateActCtxParams;
    HANDLE  ActCtxHandle;

    ASSERT(Peb->ActivationContextData == NULL);
    ASSERT(Peb->ProcessAssemblyStorageMap == NULL);
    ASSERT(Peb->SystemAssemblyStorageMap == NULL);

    CreateActCtxParams.cbSize = sizeof(CreateActCtxParams);
    CreateActCtxParams.dwFlags = (ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_SET_PROCESS_DEFAULT);
    CreateActCtxParams.lpResourceName = SXS_MANIFEST_RESOURCE_ID;
    ASSERT(MyModuleFileName[0] != 0);
    CreateActCtxParams.lpSource = MyModuleFileName;
    //
    // The error value is INVALID_HANDLE_VALUE.
    // ACTCTX_FLAG_SET_PROCESS_DEFAULT has nothing to return upon success, so it returns NULL.
    // There is nothing to cleanup upon ACTCTX_FLAG_SET_PROCESS_DEFAULT success, the data
    // is referenced in the PEB, and lasts till process shutdown.
    //
    ActCtxHandle = CreateActCtxW(&CreateActCtxParams);
    if (ActCtxHandle == INVALID_HANDLE_VALUE) {
        fSuccess = FALSE;
        SetupDebugPrint1(L"SETUP: CreateActCtxW failed in " FUNCTION L", LastError is %d\n", GetLastError());
        goto Exit;
    }
    ASSERT(ActCtxHandle == NULL);

    fSuccess = TRUE;
Exit:
    return fSuccess;
#undef FUNCTION
}

BOOL
CopySystemFiles(
    VOID
    )
{
    BOOL b;
    HINF hInf;
    HSPFILEQ FileQ;
    PVOID Context;
    WCHAR Dir[MAX_PATH];
    DWORD ScanQueueResult;

    b = FALSE;
    //hInf = SetupOpenInfFile(L"filelist.inf",NULL,INF_STYLE_WIN4,NULL);
    hInf = SyssetupInf;
    if(hInf != INVALID_HANDLE_VALUE) {

        FileQ = SetupOpenFileQueue();
        if(FileQ != INVALID_HANDLE_VALUE) {

            b =  SetupInstallFilesFromInfSection(
                     SyssetupInf,
                     NULL,
                     FileQ,
                     Win31Upgrade
                      ? L"Files.Install.CleanInstall.Win31"
                      : L"Files.Install.CleanInstall",
                     NULL,
                     BaseCopyStyle
                     );
            //
            //  Do the installation of class installers
            //  We do this here because the installation of class intallers may involve
            //  file copy. And in this case we can use the existing progress bar.
            //
            InstallPnpClassInstallers( MainWindowHandle,
                                                hInf,
                                                FileQ );

#if 0

            //
            // This feature is going away, because we're going to
            // build the delete file list using rules
            //

            if(Win95Upgrade) {
                b = b && SetupQueueDeleteSectionW(
                             FileQ,
                             hInf,
                             0,
                             L"Files.DeleteWin9x.System"
                             );

                b = b && SetupQueueDeleteSectionW(
                             FileQ,
                             hInf,
                             0,
                             L"Files.DeleteWin9x.Sysroot"
                             );

            }
#endif

            if(b) {
                b = FALSE;
                if(Context = InitSysSetupQueueCallbackEx(MainWindowHandle,
                    INVALID_HANDLE_VALUE,0,0,NULL)) {

                    if(!SetupScanFileQueue(
                           FileQ,
                           SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE,
                           MainWindowHandle,
                           NULL,
                           NULL,
                           &ScanQueueResult)) {
                            //
                            // SetupScanFileQueue should really never
                            // fail when you don't ask it to call a
                            // callback routine, but if it does, just
                            // go ahead and commit the queue.
                            //
                            ScanQueueResult = 0;
                        }


                    if( ScanQueueResult != 1 ){
                        b = SetupCommitFileQueue(MainWindowHandle,FileQ,SysSetupQueueCallback,Context);
                    }

                    TermSysSetupQueueCallback(Context);
                }
            }

            SetupCloseFileQueue(FileQ);
        }

        //SetupCloseInfFile(hInf);
    }

    return(b);
}


BOOL
UpgradeSystemFiles(
    VOID
    )
{
    BOOL b;
    HINF hInf;
    HSPFILEQ FileQ;
    PVOID Context;
    WCHAR Dir[MAX_PATH];
    DWORD ScanQueueResult;

    b = FALSE;
    //hInf = SetupOpenInfFile(L"filelist.inf",NULL,INF_STYLE_WIN4,NULL);
    hInf = SyssetupInf;
    if(hInf != INVALID_HANDLE_VALUE) {

        FileQ = SetupOpenFileQueue();
        if(FileQ != INVALID_HANDLE_VALUE) {

            b =  SetupInstallFilesFromInfSection(
                                 SyssetupInf,
                                 NULL,
                                 FileQ,
                                 Win31Upgrade
                                  ? L"Files.Install.Upgrade.Win31"
                                  : L"Files.Install.Upgrade",
                                 NULL,
                                 BaseCopyStyle
                                 );

            //
            //  Do the installation of class installers
            //  We do this here because the installation of class intallers may involve
            //  file copy. And in this case we can use the existing progress bar.
            //
            InstallPnpClassInstallers( MainWindowHandle,
                                                hInf,
                                                FileQ );

            if(b) {
                b = FALSE;
                if(Context = InitSysSetupQueueCallbackEx(MainWindowHandle,
                    INVALID_HANDLE_VALUE,0,0,NULL)) {

                    if(!SetupScanFileQueue(
                           FileQ,
                           SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE,
                           MainWindowHandle,
                           NULL,
                           NULL,
                           &ScanQueueResult)) {
                            //
                            // SetupScanFileQueue should really never
                            // fail when you don't ask it to call a
                            // callback routine, but if it does, just
                            // go ahead and commit the queue.
                            //
                            ScanQueueResult = 0;
                        }


                    if( ScanQueueResult != 1 ){
                        b = SetupCommitFileQueue(MainWindowHandle,FileQ,SysSetupQueueCallback,Context);
                    }

                    TermSysSetupQueueCallback(Context);
                }
            }

            SetupCloseFileQueue(FileQ);
        }

        //SetupCloseInfFile(hInf);
    }

    return(b);
}

VOID
MarkFilesReadOnly(
    VOID
    )
{
    WCHAR       OldCurrentDir[MAX_PATH];
    WCHAR       System32Dir[MAX_PATH];
    LPCTSTR     SectionName;
    LONG        LineCount;
    LONG        ItemNo;
    INFCONTEXT  InfContext;
    BOOL        b;


    ASSERT( SyssetupInf != INVALID_HANDLE_VALUE );

    //
    // Set current directory to system32.
    // Preserve current directory to minimize side-effects.
    //
    if(!GetCurrentDirectory(MAX_PATH,OldCurrentDir)) {
        OldCurrentDir[0] = 0;
    }
    GetSystemDirectory(System32Dir,MAX_PATH);
    SetCurrentDirectory(System32Dir);

    //
    // Now go through the list of files.
    //
    SectionName = L"Files.MarkReadOnly";
    LineCount = SetupGetLineCount( SyssetupInf, SectionName );
    for( ItemNo=0; ItemNo<LineCount; ItemNo++ ) {
        if( SetupGetLineByIndex( SyssetupInf, SectionName, ItemNo, &InfContext )) {

            b = SetFileAttributes(
                pSetupGetField( &InfContext, 0 ),
                FILE_ATTRIBUTE_READONLY );

            if (b) {
                SetupDebugPrint1( L"SETUP: Marked file %ls read-only",
                    pSetupGetField( &InfContext, 0 ) );
            } else {
                SetupDebugPrint1( L"SETUP: Could not mark file %ls read-only",
                    pSetupGetField( &InfContext, 0 ) );
            }
        }
    }

    //
    // Reset current directory and return.
    //
    if(OldCurrentDir[0]) {
        SetCurrentDirectory(OldCurrentDir);
    }
}