/*++

Copyright (c) 2001  Microsoft Corporation

Module Name:

    query.cpp

Abstract:

    Information querying functions for sxs.dll

Author:

    Michael J. Grier (MGrier) 22-May-2001

Revision History:

--*/

#include "stdinc.h"
#include <windows.h>
#include "sxsp.h"

BOOL
SxspQueryManifestInformationBasic(
    IN DWORD dwFlags,
    IN PCWSTR pszSource,
    IN DWORD dwInfoClassSpecificFlags,
    IN SIZE_T cbBuffer,
    OUT PVOID lpBuffer,
    OUT PSIZE_T cbWrittenOrRequired OPTIONAL
    );

BOOL
SxsQueryManifestInformation(
    IN DWORD dwFlags,
    IN PCWSTR pszSource,
    IN ULONG ulInfoClass,
    IN DWORD dwInfoClassSpecificFlags,
    IN SIZE_T cbBuffer,
    OUT PVOID lpBuffer,
    OUT PSIZE_T pcbWrittenOrRequired OPTIONAL
    )
{
    FN_PROLOG_WIN32;

    if (pcbWrittenOrRequired != NULL)
        *pcbWrittenOrRequired = 0;

    PARAMETER_CHECK((dwFlags & ~SXS_QUERY_MANIFEST_INFORMATION_FLAG_SOURCE_IS_DLL) == 0);
    PARAMETER_CHECK(pszSource != NULL);
    PARAMETER_CHECK(ulInfoClass == SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC);
    // ignore infoclass specific flags
    PARAMETER_CHECK((cbBuffer == 0) || (lpBuffer != NULL)); // can't have a nonzero-sized buffer with a NULL pointer to it
    PARAMETER_CHECK((cbBuffer != 0) || (pcbWrittenOrRequired != NULL));

    switch (ulInfoClass)
    {
    default:
        INTERNAL_ERROR_CHECK(
            (ulInfoClass == SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC)
            );
        ORIGINATE_WIN32_FAILURE_AND_EXIT(InternalErrorNoCaseForInfoClass, ERROR_INTERNAL_ERROR);
        break;

    case SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC:
        IFW32FALSE_EXIT(
            ::SxspQueryManifestInformationBasic(
                dwFlags,
                pszSource,
                dwInfoClassSpecificFlags,
                cbBuffer,
                lpBuffer,
                pcbWrittenOrRequired));
        break;
    }

    FN_EPILOG;
}

BOOL
SxspQueryManifestInformationBasic(
    IN DWORD dwFlags,
    IN PCWSTR pszSource,
    IN DWORD dwInfoClassSpecificFlags,
    IN SIZE_T cbBuffer,
    OUT PVOID lpBuffer,
    OUT PSIZE_T pcbWrittenOrRequired OPTIONAL
    )
{
    FN_PROLOG_WIN32;

    ACTCTXGENCTX ActCtxGenCtx;
    ULONG ManifestFlags;
    CImpersonationData ImpersonationData;
    PCWSTR Slash = NULL;
    CSmartRef<ASSEMBLY> Asm;
    CStringBuffer buffManifestPath;
    USHORT ProcessorArchitecture = ::SxspGetSystemProcessorArchitecture();
    LANGID LangId = ::GetUserDefaultUILanguage();
    CStringBufferAccessor acc;
    SIZE_T cch, cchRequired;
    SIZE_T cbRequired, cbLeft, cbWritten;
    PCASSEMBLY_IDENTITY AssemblyIdentity = NULL;
    CStringBuffer buffShortName;
    PWSTR Cursor;
    PSXS_MANIFEST_INFORMATION_BASIC psmib;
    CResourceStream DllStream;
    CFileStream FileStream;
    IStream* pStream = NULL;
    CProbedAssemblyInformation AssemblyInformation;

    if (pcbWrittenOrRequired != NULL)
        *pcbWrittenOrRequired = 0;

    // We'll assume that SxsQueryManifestInformation checked everything
    // except dwInfoClassSpecificFlags.

    PARAMETER_CHECK((dwInfoClassSpecificFlags & ~(
        SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC_FLAG_OMIT_IDENTITY |
        SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC_FLAG_OMIT_SHORTNAME)) == 0);

    //
    // If this is a DLL source, then ensure that the size is at least big enough for our basic
    // data, and ensure that the flags are zero (in this rev, that's all that's allowed.
    //
    if (dwFlags & SXS_QUERY_MANIFEST_INFORMATION_FLAG_SOURCE_IS_DLL)
    {
        PCSXS_MANIFEST_INFORMATION_SOURCE_DLL pDllSource = (PCSXS_MANIFEST_INFORMATION_SOURCE_DLL)pszSource;
        const DWORD dwValidFlags = 
            SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_LANGUAGE_VALID | 
            SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_TYPE_VALID |
            SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_ID_VALID;

        PARAMETER_CHECK(pDllSource->dwSize >= sizeof(SXS_MANIFEST_INFORMATION_SOURCE_DLL));
        PARAMETER_CHECK((pDllSource->dwFlags & ~dwValidFlags) == 0);
        PARAMETER_CHECK(pDllSource->pcwszDllPath!= NULL);
        PARAMETER_CHECK(((pDllSource->dwFlags & SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_LANGUAGE_VALID) == 0) || (pDllSource->Language != 0));
        PARAMETER_CHECK(((pDllSource->dwFlags & SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_TYPE_VALID) == 0) || (pDllSource->pcwszResourceType != NULL));
        PARAMETER_CHECK(((pDllSource->dwFlags & SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_ID_VALID) == 0) || (pDllSource->pcwszResourceName != NULL));
    }
    
    IFW32FALSE_EXIT(
        ::SxspInitActCtxGenCtx(
                &ActCtxGenCtx,         // context out
                MANIFEST_OPERATION_VALIDATE_SYNTAX,
                0,
                0,
                ImpersonationData,
                ProcessorArchitecture,
                LangId,
                ACTIVATION_CONTEXT_PATH_TYPE_NONE,
                0,
                NULL));

    IFALLOCFAILED_EXIT(Asm = new ASSEMBLY);

    //
    // For DLL sources, tease apart the version and whatnot from the
    // structure passed in.
    //
    if (dwFlags & SXS_QUERY_MANIFEST_INFORMATION_FLAG_SOURCE_IS_DLL)
    {
        PCSXS_MANIFEST_INFORMATION_SOURCE_DLL pcSourceInfo = (PCSXS_MANIFEST_INFORMATION_SOURCE_DLL)pszSource;
        IFW32FALSE_EXIT(SxspGetFullPathName(pcSourceInfo->pcwszDllPath, buffManifestPath, NULL));

        //
        // Default action... just go use the normal "find the first in the dll" thing
        //
        if (pcSourceInfo->dwFlags == 0)
        {
            IFW32FALSE_EXIT(DllStream.Initialize(buffManifestPath, (PCWSTR)RT_MANIFEST));
        }
        else
        {
            const DWORD dwFlags = pcSourceInfo->dwFlags;
            
            IFW32FALSE_EXIT(
                DllStream.Initialize(
                    buffManifestPath, 
                    ((dwFlags & SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_TYPE_VALID) ? pcSourceInfo->pcwszResourceType : (PCWSTR)RT_MANIFEST),
                    ((dwFlags & SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_ID_VALID) ? pcSourceInfo->pcwszResourceName :  NULL),
                    ((dwFlags & SXS_QUERY_MANIFEST_INFORMATION_DLL_SOURCE_FLAG_RESOURCE_LANGUAGE_VALID) ? (WORD)pcSourceInfo->Language : (WORD)MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))));
        }
          
        pStream = &DllStream;
    }
    //
    // Otherwise, dumb file.
    //
    else
    {
        IFW32FALSE_EXIT(SxspGetFullPathName(pszSource, buffManifestPath, NULL));
        IFW32FALSE_EXIT(
            FileStream.OpenForRead(
                buffManifestPath, 
                CImpersonationData(),
                FILE_SHARE_READ,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL));
        pStream = &FileStream;
    }

    ManifestFlags = ASSEMBLY_MANIFEST_FILETYPE_STREAM;            
    IFW32FALSE_EXIT(AssemblyInformation.Initialize());
    IFW32FALSE_EXIT(AssemblyInformation.SetManifestFlags(ManifestFlags));
    IFW32FALSE_EXIT(AssemblyInformation.SetManifestStream(pStream));
    IFW32FALSE_EXIT(AssemblyInformation.SetManifestPath(ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE, buffManifestPath));
    IFW32FALSE_EXIT(AssemblyInformation.SetManifestLastWriteTime(&ActCtxGenCtx));

    IFW32FALSE_EXIT(::SxspInitAssembly(Asm, AssemblyInformation));
    Asm->m_AssemblyRosterIndex = 1; // set it to be the root...
    IFW32FALSE_EXIT(::SxspIncorporateAssembly(&ActCtxGenCtx, Asm));
    IFW32FALSE_EXIT(::SxspFireActCtxGenEnding(&ActCtxGenCtx));
    pStream->Release();

    AssemblyIdentity = Asm->m_Information.GetAssemblyIdentity();

    cchRequired = 0;

    if ((dwInfoClassSpecificFlags & SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC_FLAG_OMIT_IDENTITY) == 0)
    {
        SIZE_T TextuallyEncodedIdentityBufferBytes = 0;

        IFW32FALSE_EXIT(
            ::SxsComputeAssemblyIdentityEncodedSize(
                0,
                AssemblyIdentity,
                NULL,
                SXS_ASSEMBLY_IDENTITY_ENCODING_DEFAULTGROUP_TEXTUAL,
                &TextuallyEncodedIdentityBufferBytes));
        INTERNAL_ERROR_CHECK((TextuallyEncodedIdentityBufferBytes % sizeof(WCHAR)) == 0);

        cchRequired += ((TextuallyEncodedIdentityBufferBytes / sizeof(WCHAR)) + 1);
    }

    if ((dwInfoClassSpecificFlags & SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC_FLAG_OMIT_SHORTNAME) == 0)
    {
        IFW32FALSE_EXIT(
            ::SxspGenerateSxsPath(
                SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT,
                SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY,
                NULL, 0,
                AssemblyIdentity,
                buffShortName));

        cchRequired += (buffShortName.Cch() + 1);
    }

    cbRequired = sizeof(SXS_MANIFEST_INFORMATION_BASIC) + (cchRequired * sizeof(WCHAR));

    if (cbRequired > cbBuffer)
    {
        *pcbWrittenOrRequired = cbRequired;
        ORIGINATE_WIN32_FAILURE_AND_EXIT(BufferTooSmall, ERROR_INSUFFICIENT_BUFFER);
    }

    psmib = (PSXS_MANIFEST_INFORMATION_BASIC) lpBuffer;

    psmib->lpIdentity = NULL;
    psmib->lpShortName = NULL;
    psmib->ulFileCount = ActCtxGenCtx.m_ulFileCount;

    Cursor = (PWSTR) (psmib + 1);
    cbLeft = (cbBuffer - sizeof(SXS_MANIFEST_INFORMATION_BASIC));
    cbWritten = sizeof(SXS_MANIFEST_INFORMATION_BASIC);

    if ((dwInfoClassSpecificFlags & SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC_FLAG_OMIT_IDENTITY) == 0)
    {
        SIZE_T cbActual;

        IFW32FALSE_EXIT(
            ::SxsEncodeAssemblyIdentity(
                0,
                AssemblyIdentity,
                NULL,
                SXS_ASSEMBLY_IDENTITY_ENCODING_DEFAULTGROUP_TEXTUAL,
                cbLeft,
                Cursor,
                &cbActual));

        INTERNAL_ERROR_CHECK((cbActual % sizeof(WCHAR)) == 0);
        INTERNAL_ERROR_CHECK(cbLeft >= cbActual);

        psmib->lpIdentity = Cursor;

        cbLeft -= cbActual;
        cbWritten += cbActual;

        Cursor = (PWSTR) (((ULONG_PTR) Cursor) + cbActual);

        INTERNAL_ERROR_CHECK(cbLeft >= sizeof(WCHAR));

        *Cursor++ = L'\0';
        cbLeft -= sizeof(WCHAR);
        cbWritten += sizeof(WCHAR);
    }

    if ((dwInfoClassSpecificFlags & SXS_QUERY_MANIFEST_INFORMATION_INFOCLASS_BASIC_FLAG_OMIT_SHORTNAME) == 0)
    {
        cch = buffShortName.Cch();

        INTERNAL_ERROR_CHECK(cbLeft >= ((cch + 1) * sizeof(WCHAR)));

        memcpy(Cursor, static_cast<PCWSTR>(buffShortName), (cch + 1) * sizeof(WCHAR));
        psmib->lpShortName = Cursor;

        cbLeft -= ((cch + 1) * sizeof(WCHAR));
        cbWritten += ((cch + 1) * sizeof(WCHAR));

        Cursor += (cch + 1);
    }

    if (pcbWrittenOrRequired != NULL)
        *pcbWrittenOrRequired = cbWritten;

    FN_EPILOG;
}