/*
	File:	sap.c

	Performs an upgrade of ipx sap to nt5 router 
	by munging registry values.

	Paul Mayfield, 9/3/97
*/

#include "upgrade.h"

// Structure for data passed to the enumeration 
// function.
typedef struct _SAP_ENUM_DATA {
    PSAP_IF_CONFIG pDefaults;
} SAP_ENUM_DATA;


// Globals
static WCHAR szIpxSapKey[] = 
    L"System\\CurrentControlSet\\Services\\RemoteAccess\\RouterManagers\\IPX\\RoutingProtocols\\IPXSAP\\Parameters";
static WCHAR szTempKey[] = L"DeleteMe";
static HKEY hkRouter = NULL;
static HKEY hkTemp = NULL;    
static HKEY hkIpxSap = NULL;
static PWCHAR IpxSapParams[] = 
{   
    L"SendTime", 
    L"EntryTimeout", 
    L"WANFilter", 
    L"WANUpdateTime", 
    L"MaxRecvBufferLookAhead", 
    L"RespondForInternalServers", 
    L"DelayRespondToGeneral", 
    L"DelayChangeBroadcast", 
    L"NameTableReservedHeapSize", 
    L"NameTableSortLatency", 
    L"MaxUnsortedNames", 
    L"TriggeredUpdateCheckInterval", 
    L"MaxTriggeredUpdateRequests", 
    L"ShutdownBroadcastTimeout", 
    L"RequestsPerInterface", 
    L"MinimumRequests" 
};


//
// Restore the registry from from backup and make sure 
// all global handles are opened
//
DWORD SapPrepareRegistry(
        IN PWCHAR BackupFileName) 
{
	DWORD dwErr, dwDisposition;

	// Get access to the router registry key
	dwErr = UtlAccessRouterKey(&hkRouter);
	if (dwErr != ERROR_SUCCESS) {
		PrintMessage(L"Unable to access router key.\n");
		return dwErr;
	}

	// Restore the router key from backup
	__try {
		// Open up the temporary key
		dwErr = RegCreateKeyEx(
		            hkRouter,
		            szTempKey,
		            0,
		            NULL,
		            0,
		            KEY_ALL_ACCESS,
		            NULL,
		            &hkTemp,
		            &dwDisposition);
		if (dwErr != ERROR_SUCCESS)
			return dwErr;

		// Restore the saved registry information 
		// to the temp key
		UtlSetupRestorePrivilege(TRUE);
		dwErr = RegRestoreKeyW(
		            hkTemp,
		            BackupFileName,
		            0);
		if (dwErr != ERROR_SUCCESS) 
			return dwErr;

		// Open up the ipx sap params key
		dwErr = RegCreateKeyEx(
		            HKEY_LOCAL_MACHINE,
		            szIpxSapKey,
		            0,
		            NULL,
		            0,
		            KEY_ALL_ACCESS,
		            NULL,
		            &hkIpxSap,
		            &dwDisposition);
		if (dwErr != ERROR_SUCCESS)
			return dwErr;
	}
	__finally {
		UtlSetupRestorePrivilege(FALSE);
	}
	
	return NO_ERROR;
}

//
// Cleanup the work done in the registry
//
DWORD SapCleanupRegistry() {
	if (hkIpxSap)
		RegCloseKey(hkIpxSap);
	if (hkTemp) 
		RegCloseKey(hkTemp);
	if (hkRouter) {
		RegDeleteKey(hkRouter,szTempKey);
		RegCloseKey(hkRouter);
	}
	
    hkIpxSap = NULL;
    hkTemp = NULL;
    hkRouter = NULL;
    
	return NO_ERROR;
}

//
// Restores the sap parameters that were saved before upgrade.  
// Assumes those parameters are being stored temporarily in hkTemp
//
DWORD SapRestoreParameters() {
	DWORD dwErr, dwVal;
	PWCHAR* IpxSapParamPtr = IpxSapParams;
	dwt NwSapParams;

	// Load in the parameters that were set for nwsap
	__try {
		dwErr = dwtLoadRegistyTable(&NwSapParams, hkTemp);
		if (dwErr != NO_ERROR)
			return dwErr;

		// Loop through the ipx params copying over any that applied 
		// to nwsap
		while (IpxSapParamPtr && *IpxSapParamPtr) {
		    dwErr = dwtGetValue(&NwSapParams, *IpxSapParamPtr, &dwVal);
			if (dwErr == NO_ERROR) {
				dwErr = RegSetValueEx(
				            hkIpxSap,
				            *IpxSapParamPtr,
				            0,
				            REG_DWORD,
				            (LPBYTE)&dwVal,
				            sizeof(DWORD));
				if (dwErr != ERROR_SUCCESS)
					return dwErr;
			}
			IpxSapParamPtr++;
		}
	}
	__finally {
		dwtCleanup(&NwSapParams);
	}

	return NO_ERROR;
}

//
// Installs sap in the router by initializing the 
// sap global info blob.
//
DWORD SapInstallTransportInfo(
        IN SAP_GLOBAL_INFO * pGlobal,
        IN SAP_IF_CONFIG * pIfDefaults) 
{
    LPBYTE pGlobalInfo = NULL, pDialinInfo = NULL;
    LPBYTE pNewGlobalInfo = NULL, pNewDialinInfo = NULL;
    HANDLE hConfig = NULL, hTrans = NULL;
    SAP_IF_CONFIG SapCfg, *pDialinCfg = &SapCfg;
    DWORD dwErr, dwGlobalInfoSize = 0, dwDialinInfoSize = 0;
    DWORD dwNewGlobSize = 0, dwNewDialSize = 0;

    do {
        // Connect to config server
        dwErr = MprConfigServerConnect(NULL, &hConfig);
        if (dwErr != NO_ERROR)
            break;

        // Get handle to global ipx tranport info
    	dwErr = MprConfigTransportGetHandle (
    				hConfig,
    				PID_IPX,
    				&hTrans);
        if (dwErr != NO_ERROR)
            break;

        // Get global ipx tranport info
        dwErr = MprConfigTransportGetInfo(
                    hConfig,
                    hTrans,
                    &pGlobalInfo,
                    &dwGlobalInfoSize,
                    &pDialinInfo,
                    &dwDialinInfoSize,
                    NULL);
        if (dwErr != NO_ERROR)
            break;

        // Initialize the global info blob
        dwErr = UtlUpdateInfoBlock(
                    FALSE,
                    pGlobalInfo,
                    IPX_PROTOCOL_SAP,
                    sizeof(SAP_GLOBAL_INFO),
                    1,
                    (LPBYTE)pGlobal,
                    &pNewGlobalInfo,
                    &dwNewGlobSize);
        if (dwErr != NO_ERROR) {
            if (dwErr != ERROR_ALREADY_EXISTS)
                break;
            pNewGlobalInfo = NULL;
            dwNewGlobSize = 0;
        }

        // Initialize the dialin info blob
        CopyMemory(pDialinCfg, pIfDefaults, sizeof(SAP_IF_CONFIG));
        pDialinCfg->SapIfInfo.UpdateMode = IPX_NO_UPDATE;
        dwErr = UtlUpdateInfoBlock(
                    FALSE,
                    pDialinInfo,
                    IPX_PROTOCOL_SAP,
                    sizeof(SAP_IF_CONFIG),
                    1,
                    (LPBYTE)pDialinCfg,
                    &pNewDialinInfo,
                    &dwNewDialSize);
        if (dwErr != NO_ERROR) {
            if (dwErr != ERROR_ALREADY_EXISTS)
                break;
            pNewDialinInfo = NULL;
            dwNewDialSize = 0;
        }
                            
        // Set global ipx tranport info
        dwErr = MprConfigTransportSetInfo(
                    hConfig,
                    hTrans,
                    pNewGlobalInfo,
                    dwNewGlobSize,
                    pNewDialinInfo,
                    dwNewDialSize,
                    NULL);
        if (dwErr != NO_ERROR)
            break;
        
    } while (FALSE);

    // Cleanup
    {
        if (hConfig)
            MprConfigServerDisconnect(hConfig);
        if (pGlobalInfo)
            MprConfigBufferFree(pGlobalInfo);
        if (pDialinInfo)
            MprConfigBufferFree(pDialinInfo);
        if (pNewDialinInfo)
            MprConfigBufferFree(pNewDialinInfo);
        if (pNewGlobalInfo)
            MprConfigBufferFree(pNewGlobalInfo);
    }
    
    return dwErr;
}

//
// Callback function takes an interface and updates
// its ipx sap configuration.
//
// Returns TRUE to continue the enumerate, FALSE to 
// stop it
//
DWORD SapUpgradeInterface(
        IN HANDLE hConfig,
        IN MPR_INTERFACE_0 * pIf,
        IN HANDLE hUserData)
{
    SAP_ENUM_DATA* pData = (SAP_ENUM_DATA*)hUserData;
    SAP_IF_CONFIG SapCfg, *pConfig = &SapCfg;
    LPBYTE pTransInfo=NULL, pNewTransInfo=NULL;
    HANDLE hTransport = NULL;
    DWORD dwErr, dwSize, dwNewSize = 0;

    // Validate input
    if ((hConfig == NULL) || 
        (pIf == NULL)     || 
        (pData == NULL))
    {
        return FALSE;
    }

    // Initalize the config blob
    CopyMemory(pConfig, pData->pDefaults, sizeof(SAP_IF_CONFIG));

    // Customize the update mode for the router interface
    // type
    switch (pIf->dwIfType) {
        case ROUTER_IF_TYPE_DEDICATED:
            pConfig->SapIfInfo.UpdateMode = IPX_STANDARD_UPDATE;
            break;
            
        case ROUTER_IF_TYPE_INTERNAL:
        case ROUTER_IF_TYPE_CLIENT:
            pConfig->SapIfInfo.UpdateMode = IPX_NO_UPDATE;
            break;
            
        case ROUTER_IF_TYPE_HOME_ROUTER:
        case ROUTER_IF_TYPE_FULL_ROUTER:
            pConfig->SapIfInfo.UpdateMode = IPX_AUTO_STATIC_UPDATE;
            break;
            
        case ROUTER_IF_TYPE_LOOPBACK:
        case ROUTER_IF_TYPE_TUNNEL1:
        default:
            return TRUE;
    }

    do {
        // Get the handle to ipx info associated with this if
        dwErr = MprConfigInterfaceTransportGetHandle(
                    hConfig,
                    pIf->hInterface,
                    PID_IPX,
                    &hTransport);
        if (dwErr != NO_ERROR)
            break;

        // Get the ipx info associated with this if
        dwErr = MprConfigInterfaceTransportGetInfo(
                    hConfig,
                    pIf->hInterface,
                    hTransport,
                    &pTransInfo,
                    &dwSize);
        if (dwErr != NO_ERROR)
            break;

        // Update the info block
        dwErr = UtlUpdateInfoBlock(
                    FALSE,
                    pTransInfo,
                    IPX_PROTOCOL_SAP,
                    dwSize,
                    1,
                    (LPBYTE)pConfig,
                    &pNewTransInfo,
                    &dwNewSize);
        if (dwErr != NO_ERROR) {
            if (dwErr != ERROR_ALREADY_EXISTS)
                break;
            pNewTransInfo = NULL;
            dwNewSize = 0;
        }
        
        // Commit the change
        dwErr = MprConfigInterfaceTransportSetInfo(
                    hConfig,
                    pIf->hInterface,
                    hTransport,
                    pNewTransInfo,
                    dwNewSize);
        if (dwErr != NO_ERROR)
            break;
    } while (FALSE);   

    // Cleanup
    {
        if (pNewTransInfo)
            MprConfigBufferFree(pNewTransInfo);
        if (pTransInfo)
            MprConfigBufferFree(pTransInfo);
    }

    return TRUE;
}

// 
// Installs ipx sap in the router registry tree.
//
DWORD SapInstallInRouter()
{
    DWORD dwErr;
    SAP_IF_CONFIG SapConfig, * pSap = &SapConfig;
    SAP_ENUM_DATA SapBlobs = 
    {
        pSap
    };
    SAP_GLOBAL_INFO SapGlobal = 
    {
        EVENTLOG_ERROR_TYPE         // event log mask
    };

    // Clear all structures
    ZeroMemory (pSap, sizeof(SAP_IF_CONFIG));

    // Default lan configuration
    pSap->SapIfInfo.AdminState             = ADMIN_STATE_ENABLED;
    pSap->SapIfInfo.UpdateMode             = IPX_STANDARD_UPDATE;
    pSap->SapIfInfo.PacketType             = IPX_STANDARD_PACKET_TYPE;
    pSap->SapIfInfo.Supply                 = ADMIN_STATE_ENABLED;
    pSap->SapIfInfo.Listen                 = ADMIN_STATE_ENABLED;
    pSap->SapIfInfo.GetNearestServerReply  = ADMIN_STATE_ENABLED;
    pSap->SapIfInfo.PeriodicUpdateInterval = 60;
    pSap->SapIfInfo.AgeIntervalMultiplier  = 3;
    pSap->SapIfFilters.SupplyFilterAction  = IPX_SERVICE_FILTER_DENY;
    pSap->SapIfFilters.SupplyFilterCount   = 0;
    pSap->SapIfFilters.ListenFilterAction  = IPX_SERVICE_FILTER_DENY;
    pSap->SapIfFilters.ListenFilterCount   = 0;

    // Install default sap global info
    dwErr = SapInstallTransportInfo(&SapGlobal, pSap);
    if (dwErr != NO_ERROR)
        return dwErr;

    // Enumerate the interfaces, updating each one with 
    // sap config as you go.
    dwErr = UtlEnumerateInterfaces(
                SapUpgradeInterface,
                &SapBlobs);
    if (dwErr != NO_ERROR)
        return dwErr;
        
    return NO_ERROR;
}


//
//	Performs all of the registry updating associated with an 
//  upgrade from ipx sap to router.
//
//	These are the steps:
//	1. Restore the parameters saved in FileName to szIpxSapKey.
//	2. Remove all parameters that ipx sap does not implement.
//
DWORD SapToRouterUpgrade(
        IN PWCHAR FileName) 
{
	DWORD dwErr;

	__try {
		// Restore the registry from the backup file
		dwErr = SapPrepareRegistry(FileName);
		if (dwErr != NO_ERROR)
			return dwErr;

		// Set the new registry parameters
		dwErr = SapRestoreParameters();
		if (dwErr != NO_ERROR)
			return dwErr;

	    // Install default sap global config and set default
	    // values in all router interfaces.
	    dwErr = SapInstallInRouter();
	    if (dwErr != NO_ERROR)
	        return dwErr;

		// Mark the computer as having been configured
		//
        dwErr = UtlMarkRouterConfigured();
		if (dwErr != NO_ERROR) 
		{
			PrintMessage(L"Unable to mark router as configured.\n");
			return dwErr;
		}
			
	}
	__finally {
		SapCleanupRegistry();
	}

	return NO_ERROR;
}