/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    badapps.c

Abstract:

    Implements a library for CheckBadApps key.

Author:

    Calin Negreanu (calinn)  20-Jan-1999

Revision History:

    <alias> <date> <comments>

--*/

#include <windows.h>
#include <winnt.h>
#include <shlwapi.h>
#include <badapps.h>
#include "utils.h"
#include "version.h"
#include "modules.h"

typedef struct {
    PCTSTR FileName;
    BOOL FindDataLoaded;
    WIN32_FIND_DATA FindData;
    PBYTE MappedImage;
    HANDLE FileHandle;
    HANDLE MapHandle;
    VERSION_STRUCT VersionData;
    HKEY PrevOsKey;
} FILE_DATA, *PFILE_DATA;

typedef BOOL (VERSION_CHECK_PROTOTYPE) (PFILE_DATA FileData, DWORD DataSize, PBYTE Data);
typedef VERSION_CHECK_PROTOTYPE * PVERSION_CHECK_PROTOTYPE;

typedef struct {
    DWORD   VersionId;
    PVERSION_CHECK_PROTOTYPE VersionCheck;
} VERSION_DATA, *PVERSION_DATA;

#define LIBARGS(id, fn) VERSION_CHECK_PROTOTYPE fn;
#define TOOLARGS(name, dispName, allowance, edit, query, output)

VERSION_STAMPS

#undef TOOLARGS
#undef LIBARGS

#define LIBARGS(id, fn) {id, fn},
#define TOOLARGS(name, dispName, allowance, edit, query, output)
VERSION_DATA g_VersionData [] = {
                  VERSION_STAMPS
                  {0, NULL}
                  };
#undef TOOLARGS
#undef LIBARGS

#define FD_FINDDATA     0x00000001
#define FD_MAPPINGDATA  0x00000002
#define FD_VERSIONDATA  0x00000003
#define FD_PREVOSDATA   0x00000004

BOOL
ShLoadFileData (
    IN OUT  PFILE_DATA FileData,
    IN      DWORD FileDataType
    )
{
    LONG status;
    HANDLE findHandle;
    UINT oldMode;

    switch (FileDataType) {
    case FD_FINDDATA:
        if (!FileData->FindDataLoaded) {

            oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);

            findHandle = FindFirstFile (FileData->FileName, &FileData->FindData);

            SetErrorMode(oldMode);

            if (findHandle == INVALID_HANDLE_VALUE) {
                return FALSE;
            } else {
                FindClose (findHandle);
                FileData->FindDataLoaded = TRUE;
            }
        }
        break;
    case FD_MAPPINGDATA:
        if (!FileData->MappedImage) {
            FileData->MappedImage = ShMapFileIntoMemory (
                                        FileData->FileName,
                                        &FileData->FileHandle,
                                        &FileData->MapHandle
                                        );
            if (!FileData->MappedImage) {
                return FALSE;
            }
        }
        break;
    case FD_VERSIONDATA:
        if (!FileData->VersionData.VersionBuffer) {
            if (!ShCreateVersionStruct (&FileData->VersionData, FileData->FileName)) {
                FileData->VersionData.VersionBuffer = NULL;
                return FALSE;
            }
        }
        break;
    case FD_PREVOSDATA:
        if (!FileData->PrevOsKey) {
            status = RegOpenKey (
                        HKEY_LOCAL_MACHINE,
                        S_KEY_PREVOSVERSION,
                        &FileData->PrevOsKey
                        );
            if (status != ERROR_SUCCESS) {
                return FALSE;
            }
        }
    default:
        return FALSE;
    }
    return TRUE;
}

BOOL
ShFreeFileData (
    IN OUT  PFILE_DATA FileData
    )
{
    FileData->FindDataLoaded = FALSE;
    if (FileData->MappedImage) {
        ShUnmapFile (
            FileData->MappedImage,
            FileData->FileHandle,
            FileData->MapHandle
            );
        FileData->MappedImage = NULL;
        FileData->FileHandle = NULL;
        FileData->MapHandle = NULL;
    }
    if (FileData->VersionData.VersionBuffer) {
        ShDestroyVersionStruct (&FileData->VersionData);
        FileData->VersionData.VersionBuffer = NULL;
    }
    if (FileData->PrevOsKey) {
        RegCloseKey (FileData->PrevOsKey);
        FileData->PrevOsKey = NULL;
    }
    return TRUE;
}

BOOL
ShCheckFileSize (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    if (!ShLoadFileData (FileData, FD_FINDDATA)) {
        return FALSE;
    }
    return (*((UNALIGNED DWORD*)Data) == FileData->FindData.nFileSizeLow);
}

BOOL
ShCheckModuleType (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    if (!ShLoadFileData (FileData, FD_MAPPINGDATA)) {
        return FALSE;
    }
    return (*((UNALIGNED DWORD*) Data) == ShGetModuleType (FileData->MappedImage));
}

BOOL
ShCheckBinFileVer (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONGLONG value;
    ULONGLONG mask;
    ULONGLONG currVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        currVer = 0;
    } else {
        currVer = ShVerGetFileVer (&FileData->VersionData);
    }
    value = *(UNALIGNED ULONGLONG*) Data;
    mask = *((UNALIGNED ULONGLONG*) (Data + sizeof (ULONGLONG)));
    return ((value & mask) == currVer);
}

BOOL
ShCheckBinProductVer (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONGLONG value;
    ULONGLONG mask;
    ULONGLONG currVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        currVer = 0;
    } else {
        currVer = ShVerGetProductVer (&FileData->VersionData);
    }
    value = *(UNALIGNED ULONGLONG*)Data;
    mask = *((UNALIGNED ULONGLONG*)(Data + sizeof (ULONGLONG)));
    return ((value & mask) == currVer);
}

BOOL
ShCheckUpToBinProductVer (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONGLONG ProdVerMS;
    ULONGLONG BadProdVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        ProdVerMS = 0;
    } else {
        ProdVerMS = ShVerGetProductVer (&FileData->VersionData);
    }
    BadProdVer = *(UNALIGNED ULONGLONG*)Data;
    return (ProdVerMS <= BadProdVer);
}

BOOL
ShCheckFileDateHi (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONGLONG value;
    ULONGLONG currVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        currVer = 0;
    } else {
        currVer = ShVerGetFileDateHi (&FileData->VersionData);
    }
    value = *(UNALIGNED ULONGLONG*)Data;
    return (value == currVer);
}

BOOL
ShCheckFileDateLo (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONGLONG value;
    ULONGLONG currVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        currVer = 0;
    } else {
        currVer = ShVerGetFileDateLo (&FileData->VersionData);
    }
    value = *(UNALIGNED ULONGLONG*)Data;
    return (value == currVer);
}

BOOL
ShCheckFileVerOs (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONG value;
    ULONG currVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        currVer = 0;
    } else {
        currVer = ShVerGetFileVerOs (&FileData->VersionData);
    }
    value = *(UNALIGNED ULONG*)Data;
    return (value == currVer);
}

BOOL
ShCheckFileVerType (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONG value;
    ULONG currVer;
    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        currVer = 0;
    } else {
        currVer = ShVerGetFileVerType (&FileData->VersionData);
    }
    value = *(UNALIGNED ULONG*)Data;
    return (value == currVer);
}

BOOL
ShCheckFileCheckSum (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONG value;
    if (!ShLoadFileData (FileData, FD_FINDDATA)) {
        return FALSE;
    }
    if (!ShLoadFileData (FileData, FD_MAPPINGDATA)) {
        return FALSE;
    }
    value = *(UNALIGNED ULONG*)Data;
    return (value == ShGetCheckSum (FileData->FindData.nFileSizeLow, FileData->MappedImage));
}

BOOL
ShCheckFilePECheckSum (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    ULONG value;
    if (!ShLoadFileData (FileData, FD_MAPPINGDATA)) {
        return FALSE;
    }
    value = *(UNALIGNED ULONG*)Data;
    return (value == ShGetPECheckSum (FileData->MappedImage));
}

BOOL
ShCheckStrVersion (
    IN      PFILE_DATA FileData,
    IN      PCTSTR ValueToCheck,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    BOOL result = FALSE;

    if (!ShLoadFileData (FileData, FD_VERSIONDATA)) {
        return FALSE;
    }

    {
#ifndef UNICODE
        PSTR convStr = NULL;
        INT converted = 0;
#endif
        LPCWSTR localData;
        WSTR_ALIGNED_STACK_COPY(&localData,(UNALIGNED TCHAR*)Data);

#ifndef UNICODE
        convStr = HeapAlloc (GetProcessHeap (), 0, DataSize);

        if (convStr)
        {
            converted = WideCharToMultiByte (
                            CP_ACP,
                            0,
                            localData,
                            -1,
                            convStr,
                            DataSize,
                            NULL,
                            NULL
                            );
        }

        if (!converted)
        {
            if (convStr)
                HeapFree (GetProcessHeap (), 0, convStr);

            return FALSE;
        }
        result = ShGlobalVersionCheck (&FileData->VersionData, ValueToCheck, convStr);
        HeapFree (GetProcessHeap (), 0, convStr);
#else
        result = ShGlobalVersionCheck (&FileData->VersionData, ValueToCheck, localData);       
#endif
    }

    return result;
}

BOOL
ShCheckCompanyName (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_COMPANYNAME, DataSize, Data));
}

BOOL
ShCheckProductVersion (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_PRODUCTVERSION, DataSize, Data));
}

BOOL
ShCheckProductName (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_PRODUCTNAME, DataSize, Data));
}

BOOL
ShCheckFileDescription (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_FILEDESCRIPTION, DataSize, Data));
}

BOOL
ShCheckFileVersion (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_FILEVERSION, DataSize, Data));
}

BOOL
ShCheckOriginalFileName (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_ORIGINALFILENAME, DataSize, Data));
}

BOOL
ShCheckInternalName (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_INTERNALNAME, DataSize, Data));
}

BOOL
ShCheckLegalCopyright (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    return (ShCheckStrVersion (FileData, S_VER_LEGALCOPYRIGHT, DataSize, Data));
}

BOOL
ShCheck16BitDescription (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    PTSTR value;
    BOOL result = FALSE;

    value = ShGet16ModuleDescription (FileData->MappedImage);
    if (!value) {
        return FALSE;
    }

    {
#ifndef UNICODE
        PSTR convStr = NULL;
        INT converted = 0;
#endif
        LPCWSTR localData;
        WSTR_ALIGNED_STACK_COPY(&localData,(UNALIGNED TCHAR*)Data);

#ifndef UNICODE
        convStr = HeapAlloc (GetProcessHeap (), 0, DataSize);

        if (convStr)
        {
            converted = WideCharToMultiByte (
                            CP_ACP,
                            0,
                            localData,
                            -1,
                            convStr,
                            DataSize,
                            NULL,
                            NULL
                            );
        }

        if (!converted)
        {
            if (convStr)
                HeapFree (GetProcessHeap (), 0, convStr);

            HeapFree (GetProcessHeap (), 0, value);
            return FALSE;
        }
        result = ShIsPatternMatch (convStr, value);
        HeapFree (GetProcessHeap (), 0, convStr);
#else
        result = ShIsPatternMatch (localData, value);
#endif
    }

    HeapFree (GetProcessHeap (), 0, value);
    return result;
}

BOOL
pShLoadPrevOsData (
    IN      PFILE_DATA FileData,
    IN      PCTSTR ValueName,
    OUT     PDWORD Value
    )
{
    LONG status;
    BOOL result = FALSE;
    DWORD type;
    DWORD valueSize = sizeof (DWORD);

    if (ShLoadFileData (FileData, FD_PREVOSDATA)) {

        status = RegQueryValueEx (FileData->PrevOsKey, ValueName, NULL, &type, (PBYTE)Value, &valueSize);
        if ((status == ERROR_SUCCESS) &&
            (type == REG_DWORD)
            ) {
            result = TRUE;
        }
    }
    return result;
}

BOOL
ShCheckPrevOsMajorVersion (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    BOOL result = FALSE;
    DWORD value = 0;

    if (pShLoadPrevOsData (FileData, S_VAL_MAJORVERSION, &value)) {
        result = (value == *(UNALIGNED DWORD*)(Data));
    }
    return result;
}

BOOL
ShCheckPrevOsMinorVersion (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    BOOL result = FALSE;
    DWORD value = 0;

    if (pShLoadPrevOsData (FileData, S_VAL_MINORVERSION, &value)) {
        result = (value == *(UNALIGNED DWORD*)(Data));
    }
    return result;
}

BOOL
ShCheckPrevOsPlatformId (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    BOOL result = FALSE;
    DWORD value = 0;

    if (pShLoadPrevOsData (FileData, S_VAL_PLATFORMID, &value)) {
        result = (value == *(UNALIGNED DWORD*)(Data));
    }
    return result;
}

BOOL
ShCheckPrevOsBuildNo (
    IN      PFILE_DATA FileData,
    IN      DWORD DataSize,
    IN      PBYTE Data
    )
{
    BOOL result = FALSE;
    DWORD value = 0;

    if (pShLoadPrevOsData (FileData, S_VAL_BUILDNO, &value)) {
        result = (value == *(UNALIGNED DWORD*)(Data));
    }
    return result;
}

BOOL
DoesPathExist (
    IN      PCTSTR Path
    )
{
    BOOL result = FALSE;
    DWORD errMode;

    if (Path)
    {
        errMode = SetErrorMode (SEM_FAILCRITICALERRORS);

        result = (GetFileAttributes (Path) != 0xFFFFFFFF);

        SetErrorMode(errMode);
    }

    return result;
}

BOOL
pShCheckBlob (
    IN      PCTSTR FileName,
    IN      PBYTE Blob,
    IN      BOOL QuickMode
    )
{
    FILE_DATA fileData;
    PVERSION_DATA p;
    DWORD dataId;
    DWORD dataSize;
    BOOL result = TRUE;
    PTSTR reqFile = NULL;
	PTSTR oldReqFile = NULL;
    PCTSTR filePtr = NULL;
    UINT prefixPathChars;

    ZeroMemory (&fileData, sizeof (FILE_DATA));

    fileData.FileName = FileName;
    if (!DoesPathExist (fileData.FileName)) {
        return FALSE;
    }

    filePtr = ShGetFileNameFromPath (FileName);
    if (!filePtr) {
        return FALSE;
    }

    prefixPathChars = (UINT)(filePtr - FileName);

    __try {
        dataId = *((UNALIGNED DWORD*) Blob);
        while (dataId) {
            if (dataId == VTID_REQFILE) {

                Blob += sizeof (DWORD);
                dataSize = *((UNALIGNED DWORD*) Blob);
                if (!dataSize) {
                    // should never happen
                    dataSize = 1;
                }
                Blob += sizeof (DWORD);

                // if this is the first additional file, reqFile is NULL
				oldReqFile = reqFile;

                // dataSize includes terminating nul character
                reqFile = HeapAlloc (GetProcessHeap (), 0, prefixPathChars * sizeof (TCHAR) + dataSize);

                if (!reqFile) {
                    result = FALSE;
                    __leave;
                }

                lstrcpyn (reqFile, fileData.FileName, prefixPathChars + 1);

                // if this is the first additional file, oldReqFile is NULL
				if (oldReqFile) {
                    HeapFree (GetProcessHeap (), 0, oldReqFile);
                }

                {
#ifndef UNICODE
                    PSTR convStr = NULL;
                    INT converted = 0;
#endif
                    LPCWSTR localData;
                    WSTR_ALIGNED_STACK_COPY(&localData,(UNALIGNED TCHAR*)Blob);
#ifndef UNICODE
                    convStr = HeapAlloc (GetProcessHeap (), 0, dataSize);

                    if (convStr)
                    {
                        converted = WideCharToMultiByte (
                                        CP_ACP,
                                        0,
                                        localData,
                                        -1,
                                        convStr,
                                        dataSize,
                                        NULL,
                                        NULL
                                        );
                    }

                    if (!converted)
                    {
                        if (convStr)
                            HeapFree (GetProcessHeap (), 0, convStr);

                        result = FALSE;
                        __leave;
                    }
                    lstrcpyn (reqFile + prefixPathChars, convStr, dataSize / sizeof (TCHAR));
                    HeapFree (GetProcessHeap (), 0, convStr);
#else
                    lstrcpyn (reqFile + prefixPathChars, localData, dataSize / sizeof (TCHAR));
#endif
                }

                reqFile [prefixPathChars + (dataSize / sizeof (TCHAR)) - 1] = 0;

                ShFreeFileData (&fileData);

                fileData.FileName = reqFile;

                if (!DoesPathExist (fileData.FileName)) {
                    result = FALSE;
                    __leave;
                }

                Blob += dataSize;

            } else {
                if (dataId >= VTID_LASTID) {
                    result = FALSE;
                    __leave;
                }

                p = g_VersionData + (dataId - VTID_REQFILE - 1);

                if (p->VersionId != dataId) {
                    result = FALSE;
                    __leave;
                }

                Blob += sizeof (DWORD);
                dataSize = *((UNALIGNED DWORD*) Blob);
                Blob += sizeof (DWORD);
                if (!QuickMode) {
                    if (!p->VersionCheck (&fileData, dataSize, Blob)) {
                        result = FALSE;
                        __leave;
                    }
                }
                Blob += dataSize;
            }
            dataId = *((UNALIGNED DWORD*) Blob);
        }
    }
    __finally {
        if (reqFile) {
            HeapFree (GetProcessHeap (), 0, reqFile);
        }
        ShFreeFileData (&fileData);
    }
    return result;
}

BOOL
SHIsBadApp (
    IN      PBADAPP_DATA Data,
    OUT     PBADAPP_PROP Prop
    )
{
    BOOL result = FALSE;
    PBADAPP_PROP appProp;

    __try {
        if (Data->Size != sizeof (BADAPP_DATA)) {
            return FALSE;
        }
        if (Prop->Size != sizeof (BADAPP_PROP)) {
            return FALSE;
        }
        if (*(PDWORD)(Data->Blob) != sizeof (BADAPP_PROP)) {
            return FALSE;
        }
        if (pShCheckBlob (Data->FilePath, Data->Blob + sizeof (BADAPP_PROP), TRUE)) {
            result = pShCheckBlob (Data->FilePath, Data->Blob + sizeof (BADAPP_PROP), FALSE);
        }
        if (result) {
            appProp = (PBADAPP_PROP) Data->Blob;
            Prop->MsgId = appProp->MsgId;
            Prop->AppType = appProp->AppType;
        }
    }
    __except (1) {
        result = FALSE;
    }
    return result;
}