//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1998.
//
//  File:       boscomp.cpp
//
//  Contents:   DllMain
//
//  Notes:      modified from windows\setup\winnt32\apmupgrd.cpp by wnelson
//
//  Author:     wnelson		2 Apr 99
//              
//              ShaoYin     9 Sep 99 revised, add support for Exchange Server
//
//----------------------------------------------------------------------------

#include <winnt32.h>
#include "boscomp.h"
#include "resource.h"

HINSTANCE g_hinst;

// help text files
TCHAR szErrorHTM[] = TEXT("compdata\\boserror.htm");
TCHAR szErrorTXT[] = TEXT("compdata\\boserror.txt");

// help text files (Exchange Server)
TCHAR szExchangeHTM[] = TEXT("compdata\\exchange.htm");
TCHAR szExchangeTXT[] = TEXT("compdata\\exchange.txt");


// bos/sbs 4.5 reg keys, value names, and possible values
const TCHAR szBosKey[] = TEXT("Software\\Microsoft\\Backoffice");
const TCHAR szFamilyIdKey[] = TEXT("FamilyID");
const TCHAR szBosFamilyId[] = TEXT("8D4BCD88-3236-11d2-AB4E-00C04FB1799F");
const TCHAR szSbsFamilyId[] = TEXT("EE2D3727-33C0-11d2-AB50-00C04FB1799F");
const TCHAR szSuiteVersionKey[] = TEXT("SuiteVersion");
const TCHAR szSuiteNameKey[] = TEXT("SuiteName");
const TCHAR sz45Version[] = TEXT("4.5");
const TCHAR szBosName[] = TEXT("BackOffice Server");
const TCHAR szSbsName[] = TEXT("Small Business Server");

// sbs 4.0x reg keys, value names, and values
const TCHAR szSbsKey[] = TEXT("Software\\Microsoft\\Small Business");
const TCHAR szSbsVersionKey[] = TEXT("Version");
const TCHAR szSbs40AVersion[] = TEXT("4.0a");
const TCHAR szProductOptionsKey[] = TEXT("System\\CurrentControlSet\\Control\\ProductOptions");
//const TCHAR szProductOptionsKey[] = TEXT("software\\test");
const TCHAR szProductSuiteKey[] = TEXT("ProductSuite");
const TCHAR szSbsProductSuiteValue[] = TEXT("Small Business");
const TCHAR szSbsRestrictedProductSuiteValue[] = TEXT("Small Business(Restricted)");

// bos 4.0 version 
TCHAR szBos40VersionKey[] = TEXT("Version");
TCHAR szBos40Version[] = TEXT("4.0");

// bos 2.5 key
TCHAR  szBos25Key[] = TEXT("2.5"); 



// Exchange 5.5 reg keys, value names. 
const TCHAR szExchangeKey[] = TEXT("Software\\Microsoft\\Exchange\\Setup");
const TCHAR szExchangeVerKey[] = TEXT("NewestBuild");
const DWORD dwExchangeVer55 = 0x7a8;

// Exchange 5.5 Server Pack reg key and value name.
const TCHAR szExchangeSvcPackKey[] = TEXT("ServicePackBuild");
const DWORD dwExchangeSvcPack3 = 0xa5a;


//+---------------------------------------------------------------------------
//
//  Function:   DllMain
//
//  Purpose:    constructor
//
//  Arguments:  Standard DLL entry point arguments
//
//  Author:     wnelson     2 Apr 99
//
//  Notes:      from kumarp    12 April 97
//
extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance,
                    DWORD dwReasonForCall,
                    LPVOID lpReserved)
{
    BOOL status = TRUE;

    switch( dwReasonForCall )
    {
    case DLL_PROCESS_ATTACH:
        {
	   g_hinst = hInstance;
	   DisableThreadLibraryCalls(hInstance);
        }
    break;

    case DLL_PROCESS_DETACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    }

    return status;
}


//+----------------------------------------------------------------------
//
//  Function:  BosHardBlockCheck
//
//  Purpose:   This function is called by winnt32.exe so that we
//             can check for installed bos/sbs suites that cannot be upgraded to Win2k.
//
//             
//
//  Arguments: 
//     CompatibilityCallback [in]  pointer to COMPATIBILITYCALLBACK fn
//     Context               [in]  pointer to compatibility context
//
//  FALSE if Win2k setup can continue.
//  TRUE if Win2k cannot upgrade the installed suite.
// 
//  Author:    wnelson     2 Apr 99
//
//  Notes: 
//  TRUE is returned in the following cases: BOS 2.5; SBS 4.0,4.0a; BOS 4.0
//	For SBS, it does not matter if they've upgraded to full NT Server; they still have to
//	upgrade their suite to BackOffice 4.5 in order to continue.
//

BOOL WINAPI BosHardBlockCheck(IN PCOMPAIBILITYCALLBACK CompatibilityCallback,IN LPVOID Context)
{
	SuiteVersion eVersion=DetermineInstalledSuite();
	if (eVersion==VER_NONE || eVersion==VER_BOS45 || eVersion==VER_POST45) return FALSE;
	TCHAR szMsg[1024];
	GetSuiteMessage(eVersion,szMsg,1024);
   
	// Use the callback function to send the signal
	COMPATIBILITY_ENTRY ce;
	ZeroMemory(&ce,sizeof(COMPATIBILITY_ENTRY));
	ce.Description = szMsg;
	ce.HtmlName = szErrorHTM; // defined above
	ce.TextName = szErrorTXT; // defined above
	ce.RegKeyName = NULL;
	ce.RegValName = NULL;
	ce.RegValDataSize = 0;
	ce.RegValData = NULL;
	ce.SaveValue = NULL;
	ce.Flags = 0;
	CompatibilityCallback(&ce, Context);

   return TRUE;
}
//+----------------------------------------------------------------------
//
//  Function:  BosSoftBlockCheck
//
//  Purpose:   This function is called by winnt32.exe so that we
//             can check for an installed sbs/bos suite with possible upgrade problems.
//
//             
//
//  Arguments: 
//     CompatibilityCallback [in]  pointer to COMPATIBILITYCALLBACK fn
//     Context               [in]  pointer to compatibility context
//
//  FALSE if Win2k setup can continue.
//  TRUE if Win2k setup needs to warn the user that upgrade may impair functionality of installed suite.
// 
//  Author:    wnelson     2 Apr 99
//
//             ShaoYin     9 Sep 99, add support for Exchange Server
//
//  Notes: 
//  TRUE is returned in the following cases: BOS 4.5
//
BOOL WINAPI BosSoftBlockCheck(IN PCOMPAIBILITYCALLBACK CompatibilityCallback,IN LPVOID Context)
{

    BOOL result = FALSE;
    
	SuiteVersion eVersion=DetermineInstalledSuite();
	if (eVersion==VER_BOS45)
	{
		TCHAR szMsg[1024];
		GetSuiteMessage(eVersion,szMsg,1024);
	
		// Use the callback function to send the signal
		COMPATIBILITY_ENTRY ce;
		ZeroMemory(&ce,sizeof(COMPATIBILITY_ENTRY));
		ce.Description = szMsg;
		ce.HtmlName = szErrorHTM; // defined above
		ce.TextName = szErrorTXT; // defined above
		ce.RegKeyName = NULL;
		ce.RegValName = NULL;
		ce.RegValDataSize = 0;
		ce.RegValData = NULL;
		ce.SaveValue = NULL;
		ce.Flags = 0;
		CompatibilityCallback(&ce, Context);

        result = TRUE;
	}

    ExchangeVersion exVersion = DetermineExchangeVersion();
    if (exVersion == EXCHANGE_VER_PRE55SP3)
    {
        TCHAR szMsgExchange[1024];
        COMPATIBILITY_ENTRY cExchange;

        LoadResString(IDS_Exchange, szMsgExchange, 1024);
        ZeroMemory(&cExchange, sizeof(COMPATIBILITY_ENTRY));
        cExchange.Description = szMsgExchange;
        cExchange.HtmlName = szExchangeHTM;
        cExchange.TextName = szExchangeTXT;
        cExchange.RegKeyName = NULL;
        cExchange.RegValName = NULL;
        cExchange.RegValData = NULL;
        cExchange.RegValDataSize = 0;
        cExchange.SaveValue = NULL;
        cExchange.Flags = 0;
        CompatibilityCallback(&cExchange, Context);

        result = TRUE;
    }

   return( result );
}


SuiteVersion DetermineInstalledSuite()
{
	SuiteVersion eVersion=VER_NONE;
	HKEY hKey,hKey25;
	TCHAR szFamilyId[256], szVersion[256], szSuiteName[256];
	DWORD dwVerLen1=256, dwVerLen2=256, dwNameLen=256, dwIdLen=256;
	DWORD dwDataType=REG_SZ;
	
	//
	// First look for versions using the bos key (i.e., all bos versions and sbs 4.5 or later)
	//
	if (ERROR_SUCCESS==RegOpenKey(HKEY_LOCAL_MACHINE,szBosKey,&hKey))
	{
		if(ERROR_SUCCESS==RegQueryValueEx(hKey,szSuiteVersionKey,0,&dwDataType,(LPBYTE)szVersion,&dwVerLen1))
		{
			if(0==_tcsicmp(szVersion,sz45Version))
			{
				// Some 4.5 version is on the box.
				if (ERROR_SUCCESS==RegQueryValueEx(hKey,szFamilyIdKey,0,&dwDataType,(LPBYTE)szFamilyId,&dwIdLen))
				{
					if (0==_tcsicmp(szFamilyId,szBosFamilyId) )
					{
						eVersion=VER_BOS45;
					}
					else if (0==_tcsicmp(szFamilyId,szSbsFamilyId) )
					{
						eVersion=VER_SBS45;
					}
				}
				else
				{
					// The guid just checked is the official version marker; however, 4.5 beta 1 did
					// not use the guids. This version is timebombed and should be dead, but just in case
					// we'll check the suite name string also.
					if (ERROR_SUCCESS==RegQueryValueEx(hKey,szSuiteNameKey,0,&dwDataType, (LPBYTE)szSuiteName,&dwNameLen))
					{
						if (0==_tcsicmp(szSuiteName,szBosName))
						{
							eVersion=VER_BOS45;
						}
						else if (0==_tcsicmp(szSuiteName,szSbsName))
						{
							eVersion=VER_SBS45;
						}
					}
				}
			}
			else
			{
				// A later version is on the box
				eVersion=VER_POST45;
			}
		}
		// look for bos 4.0
		else if (ERROR_SUCCESS==RegQueryValueEx(hKey,szBos40VersionKey,0,&dwDataType,(LPBYTE)szVersion,&dwVerLen2) && 0==_tcsicmp(szBos40Version,szVersion))
		{
			eVersion=VER_BOS40;
		}
		// look for bos 2.5
		else if (ERROR_SUCCESS==RegOpenKey(hKey,szBos25Key,&hKey25))
		{
			eVersion=VER_BOS25;
			RegCloseKey(hKey25);
		}
		RegCloseKey(hKey);
		
	}
	//
	// Look for SBS versions 4.0a, and 4.0.
	//
	else if (ProductSuiteContains(szSbsProductSuiteValue))
	{
		//	
		// If we get here, SBS 4.0 or 4.0a is on the box.
		//
		if (ERROR_SUCCESS==RegOpenKey(HKEY_LOCAL_MACHINE,szSbsKey,&hKey))
		{
			TCHAR szVersion[256];
			DWORD dwVerLen=256;
			DWORD dwDataType=REG_SZ;
			if (ERROR_SUCCESS==RegQueryValueEx(hKey,szSbsVersionKey,0,&dwDataType,(LPBYTE)szVersion,&dwVerLen) &&
				0==_tcsicmp(szVersion,szSbs40AVersion))
			{
				eVersion=VER_SBS40A;	
			}
			else
			{
				eVersion=VER_SBS40;
			}
			RegCloseKey(hKey);
		}
	}
	// We have to make sure the user isn't tricking us into allowing Win2k to upgrade
	// restricted sbs nt. 
	if (eVersion==VER_NONE || eVersion==VER_BOS45 || eVersion==VER_POST45)
	{
		if (ProductSuiteContains(szSbsRestrictedProductSuiteValue))
		{
			// User tried to fool us by altering version info. 
			eVersion=VER_SBSREST;
		}
	}
	return eVersion;
}

// return the display string for the version in question.
void GetSuiteMessage(SuiteVersion eSV, TCHAR* szMsg, UINT nLen)
{
	szMsg[0]=0;
	switch (eSV)
	{
		case VER_NONE:
			break;
		case VER_BOS45:
			LoadResString(IDS_Bos45Msg,szMsg,nLen);
			break;	
		case VER_BOS40:
			LoadResString(IDS_Bos40Msg,szMsg,nLen);
			break;	
		case VER_BOS25:
			LoadResString(IDS_Bos25Msg,szMsg,nLen);
			break;	
		case VER_SBS45:
			LoadResString(IDS_Sbs45Msg,szMsg,nLen);
			break;	
		case VER_SBS40:
			LoadResString(IDS_Sbs40Msg,szMsg,nLen);
			break;	
		case VER_SBS40A:
			LoadResString(IDS_Sbs40AMsg,szMsg,nLen);
			break;
		case VER_SBSREST:	
			LoadResString(IDS_SbsRestMsg,szMsg,nLen);
			break;
	}		
}
void LoadResString(UINT nRes, TCHAR* szString, UINT nLen)
{
	if(!LoadString(g_hinst, nRes, szString, nLen)) 
	{
		szString[0] = 0;
	}
}

// Check the ProductOptions\ProductSuite multi-sz value for the string szTest.
bool ProductSuiteContains(const TCHAR* szTest)
{
	bool bRet=false;
	HKEY hKey;
	unsigned char* Value=NULL;
	TCHAR* szValue;
	DWORD dwDataLen=0;
	DWORD dwDataType=REG_MULTI_SZ;

	if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE,szProductOptionsKey,&hKey) )
	{
		// see how big the data will be
		if (ERROR_SUCCESS ==  RegQueryValueEx(hKey,szProductSuiteKey,0,&dwDataType,NULL,&dwDataLen))
		{
			Value=new unsigned char[dwDataLen]; 
			if (Value != NULL && dwDataLen != 1) // if the multi-sz is empty, we get back dataLen=1 and don't need to go further
			{
				if (RegQueryValueEx(hKey,szProductSuiteKey,0,&dwDataType,Value,&dwDataLen) == ERROR_SUCCESS)
				{
					szValue=(TCHAR*)Value;

					for (UINT n = 1 ; *szValue != 0 ; szValue += _tcslen(szValue) + 1) 
					{
						if ( _tcsstr(szValue, szTest) != 0)
						{
							bRet=true;
							break;
						}
					}

				}
			}
			if (Value != NULL)	delete[] Value;
		}
		RegCloseKey(hKey);
	}
	return bRet;
}




ExchangeVersion DetermineExchangeVersion()
{
    ExchangeVersion eVersion=EXCHANGE_VER_NONE;
    HKEY hExchangeKey;
    DWORD dwCurrentVer, dwCurrentSvcPackVer;
    DWORD dwVerLen=sizeof(DWORD);
    DWORD dwDataType=REG_DWORD;
	
    //
    // First look for versions using the Exchange key
    //
    if (ERROR_SUCCESS==RegOpenKey(HKEY_LOCAL_MACHINE,szExchangeKey,&hExchangeKey))
    {
        if(ERROR_SUCCESS==RegQueryValueEx(hExchangeKey,szExchangeVerKey,0,&dwDataType,(LPBYTE)&dwCurrentVer,&dwVerLen))
        {
            if (dwCurrentVer < dwExchangeVer55)
            {
                // prior Exchange 5.5
                eVersion=EXCHANGE_VER_PRE55SP3;
            }
            else if (dwCurrentVer == dwExchangeVer55)
            {
                // Exchange 5.5 version is on the box.
                if (ERROR_SUCCESS==RegQueryValueEx(hExchangeKey,szExchangeSvcPackKey,0,&dwDataType,(LPBYTE)&dwCurrentSvcPackVer,&dwVerLen))
                {
                    if (dwCurrentSvcPackVer >= dwExchangeSvcPack3)
                    {
                        // has Service Pack 3 installed
                        eVersion=EXCHANGE_VER_POST55SP3;
                    }
                    else
                    {
                        // no Service Pack 3
                        eVersion=EXCHANGE_VER_PRE55SP3;
                    }
                }
                else
                {
                    // no Servie Pack 3
                    eVersion=EXCHANGE_VER_PRE55SP3;
                }
            }
        }
        RegCloseKey(hExchangeKey);
    }

    return eVersion;
}