//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1999  Microsoft Corporation
//
// Module Name:
//
//    utils.c
//
// Abstract:
//
//      utils functions
//
// Revision History:
//  
//    Thierry Perraut 04/07/1999
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"

#include <netsh.h>
#include "aaaamontr.h"
#include "strdefs.h"
#include "rmstring.h"
#include "aaaamon.h"
#include "context.h"
#include <rtutils.h>
#include "utils.h"
#include "base64tool.h"

const WCHAR c_szCurrentBuildNumber[]      = L"CurrentBuildNumber";
const WCHAR c_szWinVersionPath[]          =
    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
const WCHAR c_szAssignFmt[]               = L"%s = %s";
const WCHAR pszAAAAEngineParamStub[]   = 
    L"SYSTEM\\CurrentControlSet\\Services\\AAAAEngine\\Parameters\\";

AAAA_ALLOC_FN               RutlAlloc;
AAAA_DWORDDUP_FN            RutlDwordDup;
AAAA_CREATE_DUMP_FILE_FN    RutlCreateDumpFile;
AAAA_CLOSE_DUMP_FILE_FN     RutlCloseDumpFile;
AAAA_ASSIGN_FROM_TOKENS_FN  RutlAssignmentFromTokens;
AAAA_STRDUP_FN              RutlStrDup;
AAAA_FREE_FN                RutlFree;
AAAA_GET_OS_VERSION_FN      RutlGetOsVersion;
AAAA_GET_TAG_TOKEN_FN       RutlGetTagToken;
AAAA_IS_HELP_TOKEN_FN       RutlIsHelpToken;



//////////////////////////////////////////////////////////////////////////////
// RutlGetTagToken
// 
// Routine Description:
// 
//     Identifies each argument based on its tag. It assumes that each argument
//     has a tag. It also removes tag= from each argument.
// 
// Arguments:
// 
//     ppwcArguments  - The argument array. Each argument has tag=value form
//     dwCurrentIndex - ppwcArguments[dwCurrentIndex] is first arg.
//     dwArgCount     - ppwcArguments[dwArgCount - 1] is last arg.
//     pttTagToken    - Array of tag token ids that are allowed in the args
//     dwNumTags      - Size of pttTagToken
//     pdwOut         - Array identifying the type of each argument.
// 
// Return Value:
// 
//     NO_ERROR, ERROR_INVALID_PARAMETER, ERROR_INVALID_OPTION_TAG
// 
//////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
RutlGetTagToken(
                IN  HANDLE      hModule,
                IN  PWCHAR      *ppwcArguments,
                IN  DWORD       dwCurrentIndex,
                IN  DWORD       dwArgCount,
                IN  PTAG_TYPE   pttTagToken,
                IN  DWORD       dwNumTags,
                OUT PDWORD      pdwOut
               )
{
    PWCHAR     pwcTag,pwcTagVal,pwszArg = NULL;

    //
    // This function assumes that every argument has a tag
    // It goes ahead and removes the tag.
    //

    for ( DWORD i = dwCurrentIndex; i < dwArgCount; ++i )
    {
        DWORD len = wcslen(ppwcArguments[i]);

        if ( !len )
        {
            //
            // something wrong with arg
            //

            pdwOut[i] = static_cast<DWORD> (-1);
            continue;
        }

        pwszArg = static_cast<unsigned short *>(RutlAlloc(
                                                   (len + 1) * sizeof(WCHAR),
                                                   FALSE));

        if ( !pwszArg )
        {
            DisplayError(NULL, ERROR_NOT_ENOUGH_MEMORY);
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        wcscpy(pwszArg, ppwcArguments[i]);

        pwcTag = wcstok(pwszArg, NETSH_ARG_DELIMITER);

        //
        // Got the first part
        // Now if next call returns NULL then there was no tag
        //

        pwcTagVal = wcstok((PWCHAR)NULL,  NETSH_ARG_DELIMITER);

        if ( !pwcTagVal )
        {
            DisplayMessage(g_hModule, 
                           ERROR_NO_TAG,
                           ppwcArguments[i]);

            RutlFree(pwszArg);

            return ERROR_INVALID_PARAMETER;
        }

        //
        // Got the tag. Now try to match it
        //

        BOOL bFound = FALSE;
        pdwOut[i - dwCurrentIndex] = (DWORD) -1;

        for ( DWORD j = 0; j < dwNumTags; ++j )
        {
            if ( MatchToken(pwcTag, pttTagToken[j].pwszTag) )
            {
                //
                // Tag matched
                //

                bFound = TRUE;
                pdwOut[i - dwCurrentIndex] = j;
                break;
            }
        }

        if ( bFound )
        {
            //
            // Remove tag from the argument
            //

            wcscpy(ppwcArguments[i], pwcTagVal);
        }
        else
        {
            DisplayError(NULL,
                         ERROR_INVALID_OPTION_TAG, 
                         pwcTag);

            RutlFree(pwszArg);

            return ERROR_INVALID_OPTION_TAG;
        }

        RutlFree(pwszArg);
    }

    return NO_ERROR;
}


//////////////////////////////////////////////////////////////////////////////
// RutlCreateDumpFile
//////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
RutlCreateDumpFile(
                   IN  PWCHAR  pwszName,
                   OUT PHANDLE phFile
                  )
{
    HANDLE  hFile;

    *phFile = NULL;

    // Create/open the file
    hFile = CreateFileW(pwszName,
                        GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_DELETE,
                        NULL,
                        OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);

    if ( hFile == INVALID_HANDLE_VALUE )
    {
        return GetLastError();
    }

    // Go to the end of the file
    SetFilePointer(hFile, 0, NULL, FILE_END);    

    *phFile = hFile;

    return NO_ERROR;
}


//////////////////////////////////////////////////////////////////////////////
// RutlCloseDumpFile
//////////////////////////////////////////////////////////////////////////////
VOID
WINAPI
RutlCloseDumpFile(HANDLE  hFile)
{
    CloseHandle(hFile);
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlAlloc
//
// Returns an allocated block of memory conditionally
// zeroed of the given size.
//
//////////////////////////////////////////////////////////////////////////////
PVOID 
WINAPI
RutlAlloc(
    IN DWORD    dwBytes,
    IN BOOL     bZero
    )
{
    DWORD dwFlags = 0;

    if ( bZero )
    {
        dwFlags |= HEAP_ZERO_MEMORY;
    }

    return HeapAlloc(GetProcessHeap(), dwFlags, dwBytes);
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlFree
//
// Conditionally free's a pointer if it is non-null
//
//////////////////////////////////////////////////////////////////////////////
VOID 
WINAPI
RutlFree(
            IN PVOID pvData
        )
{
    HeapFree(GetProcessHeap(), 0, pvData);
}


//////////////////////////////////////////////////////////////////////////////
// 
// RutlStrDup
// 
// Uses RutlAlloc to copy a string
//
//////////////////////////////////////////////////////////////////////////////
PWCHAR
WINAPI
RutlStrDup(
            IN PWCHAR pwszSrc
          )
{
    PWCHAR  pszRet = NULL;
    DWORD   dwLen; 
    
    if (( !pwszSrc ) || ((dwLen = wcslen(pwszSrc)) == 0))
    {
        return NULL;
    }

    pszRet = static_cast<PWCHAR>(RutlAlloc((dwLen + 1) * sizeof(WCHAR),FALSE));
    if ( pszRet )
    {
        wcscpy(pszRet, pwszSrc);
    }

    return pszRet;
}


//////////////////////////////////////////////////////////////////////////////
// RutlDwordDup
// 
// Uses RutlAlloc to copy a dword
//
//////////////////////////////////////////////////////////////////////////////
LPDWORD
WINAPI
RutlDwordDup(
              IN DWORD dwSrc
            )
{
    LPDWORD lpdwRet = NULL;
    
    lpdwRet = static_cast<LPDWORD>(RutlAlloc(sizeof(DWORD), FALSE));
    if ( lpdwRet )
    {
        *lpdwRet = dwSrc;
    }

    return lpdwRet;
}

    
//////////////////////////////////////////////////////////////////////////////
//
// RutlGetOsVersion
//
// Returns the build number of operating system
//
//////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
RutlGetOsVersion(
    IN  PWCHAR  pwszRouter, 
    OUT LPDWORD lpdwVersion)
{

    DWORD   dwErr, dwType = REG_SZ, dwLength;
    HKEY    hkMachine = NULL, hkVersion = NULL;
    WCHAR   pszBuildNumber[64];
    PWCHAR  pszMachine = pwszRouter;

    //
    // Validate and initialize
    //
    if ( !lpdwVersion ) 
    { 
        return ERROR_INVALID_PARAMETER; 
    }
    *lpdwVersion = FALSE;

    do 
    {
        //
        // Connect to the remote server
        //
        dwErr = RegConnectRegistry(
                    pszMachine,
                    HKEY_LOCAL_MACHINE,
                    &hkMachine);
        if ( dwErr != ERROR_SUCCESS )        
        {
            break;
        }

        //
        // Open the windows version key
        //

        dwErr = RegOpenKeyEx(
                    hkMachine, 
                    c_szWinVersionPath, 
                    0, 
                    KEY_QUERY_VALUE, 
                    &hkVersion
                    );
        if ( dwErr != NO_ERROR ) 
        { 
            break; 
        }

        //
        // Read in the current version key
        //
        dwLength = sizeof(pszBuildNumber);
        dwErr = RegQueryValueEx (
                    hkVersion, 
                    c_szCurrentBuildNumber, 
                    NULL, 
                    &dwType,
                    (BYTE*)pszBuildNumber, 
                    &dwLength
                    );
        if ( dwErr != NO_ERROR ) 
        { 
            break; 
        }

        *lpdwVersion = static_cast<DWORD>(wcstol(pszBuildNumber, NULL, 10));
        
    } while (FALSE);


    // Cleanup
    if ( hkVersion )
    {
        RegCloseKey( hkVersion );
    }
    if ( hkMachine )
    {
        RegCloseKey( hkMachine );
    }

    return dwErr;
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlParseOptions
// 
// Routine Description:
// 
//     Based on an array of tag types returns which options are
//     included in the given command line.
// 
// Arguments:
// 
//     ppwcArguments   - Argument array
//     dwCurrentIndex  - ppwcArguments[dwCurrentIndex] is the first arg
//     dwArgCount      - ppwcArguments[dwArgCount - 1] is the last arg
// 
// Return Value:
// 
//     NO_ERROR
// 
//////////////////////////////////////////////////////////////////////////////
DWORD 
WINAPI
RutlParseOptions(
                    IN  PWCHAR*                 ppwcArguments,
                    IN  DWORD                   dwCurrentIndex,
                    IN  DWORD                   dwArgCount,
                    IN  DWORD                   dwNumArgs,
                    IN  TAG_TYPE*               rgTags,
                    IN  DWORD                   dwTagCount,
                    OUT LPDWORD*                ppdwTagTypes
                )
{
    LPDWORD     pdwTagType;
    DWORD       i, dwErr = NO_ERROR;
    
    // If there are no arguments, there's nothing to to
    //
    if ( !dwNumArgs )
    {   
        return NO_ERROR;
    }

    // Set up the table of present options
    pdwTagType = static_cast<LPDWORD>(RutlAlloc(
                                                 dwArgCount * sizeof(DWORD), 
                                                 TRUE
                                               ));
    if( !pdwTagType )
    {
        DisplayError(NULL, ERROR_NOT_ENOUGH_MEMORY);
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    do {
        //
        // The argument has a tag. Assume all of them have tags
        //
        if( wcsstr(ppwcArguments[dwCurrentIndex], NETSH_ARG_DELIMITER) )
        {
            dwErr = RutlGetTagToken(
                        g_hModule, 
                        ppwcArguments,
                        dwCurrentIndex,
                        dwArgCount,
                        rgTags,
                        dwTagCount,
                        pdwTagType);

            if( dwErr != NO_ERROR )
            {
                if( dwErr == ERROR_INVALID_OPTION_TAG )
                {
                    dwErr = ERROR_INVALID_SYNTAX;
                    break;
                }
            }
        }
        else
        {
            //
            // No tags - all args must be in order
            //
            for( i = 0; i < dwNumArgs; ++i )
            {
                pdwTagType[i] = i;
            }
        }
        
    } while (FALSE);        

    // Cleanup
    {
        if ( dwErr == NO_ERROR )
        {
            *ppdwTagTypes = pdwTagType;
        }
        else
        {
            RutlFree(pdwTagType);
        }
    }

    return dwErr;
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlIsHelpToken
//
//////////////////////////////////////////////////////////////////////////////
BOOL
WINAPI
RutlIsHelpToken(PWCHAR  pwszToken)
{
    if( MatchToken(pwszToken, CMD_AAAA_HELP1) )
    {
        return TRUE;
    }

    if( MatchToken(pwszToken, CMD_AAAA_HELP2) )
    {
        return TRUE;
    }

    return FALSE;
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlAssignmentFromTokens
//
//////////////////////////////////////////////////////////////////////////////
PWCHAR
WINAPI
RutlAssignmentFromTokens(
                            IN HINSTANCE hModule,
                            IN PWCHAR pwszToken,
                            IN PWCHAR pszString
                        )
{
    PWCHAR pszRet = NULL, pszCmd = NULL;
    DWORD dwErr = NO_ERROR, dwSize;
    
    do 
    {
        pszCmd = pwszToken;

        // Compute the string lenghth needed
        //
        dwSize = wcslen(pszString)      + 
                 wcslen(pszCmd)         + 
                 wcslen(c_szAssignFmt)  + 
                 1;
        dwSize *= sizeof(WCHAR);

        // Allocate the return value
        pszRet = (PWCHAR) RutlAlloc(dwSize, FALSE);
        if (pszRet == NULL)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        // Copy in the command assignment
        _snwprintf(
                    pszRet, 
                    dwSize,
                    c_szAssignFmt, 
                    pszCmd, 
                    pszString
                  );

    } while ( FALSE );

    // Cleanup
    {
        if ( dwErr != NO_ERROR )
        {
            if ( pszRet )
            {
                RutlFree(pszRet);
            }
            pszRet = NULL;
        }
    }
    return pszRet;
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlRegReadDword
//
//////////////////////////////////////////////////////////////////////////////
DWORD
RutlRegReadDword(
                    IN  HKEY hKey,
                    IN  PWCHAR pszValName,
                    OUT LPDWORD lpdwValue
                )
{
    DWORD dwSize = sizeof(DWORD), dwType = REG_DWORD, dwErr;

    dwErr = RegQueryValueExW(
                hKey,
                pszValName,
                NULL,
                &dwType,
                (LPBYTE)lpdwValue,
                &dwSize);
    if ( dwErr == ERROR_FILE_NOT_FOUND )
    {
        dwErr = NO_ERROR;
    }

    return dwErr;                
}                
        

//////////////////////////////////////////////////////////////////////////////
//
// RutlRegReadString
//
//////////////////////////////////////////////////////////////////////////////
DWORD
RutlRegReadString(
                    IN  HKEY hKey,
                    IN  PWCHAR pszValName,
                    OUT PWCHAR* ppszValue
                 )
{
    DWORD dwErr = NO_ERROR, dwSize = 0;

    *ppszValue = NULL;
    
    // Findout how big the buffer should be
    //
    dwErr = RegQueryValueExW(
                hKey,
                pszValName,
                NULL,
                NULL,
                NULL,
                &dwSize);
    if ( dwErr == ERROR_FILE_NOT_FOUND )
    {
        return NO_ERROR;
    }
    if ( dwErr != ERROR_SUCCESS )
    {
        return dwErr;
    }

    // Allocate the string
    //
    *ppszValue = (PWCHAR) RutlAlloc(dwSize, TRUE);
    if ( ! *ppszValue )
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    // Read the value in and return 
    //
    dwErr = RegQueryValueExW(
                hKey,
                pszValName,
                NULL,
                NULL,
                (LPBYTE)*ppszValue,
                &dwSize);
                
    return dwErr;
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlRegReadString
//
//////////////////////////////////////////////////////////////////////////////
DWORD
RutlRegWriteDword(
                    IN HKEY hKey,
                    IN PWCHAR pszValName,
                    IN DWORD dwValue
                 )
{
    return RegSetValueExW(
                hKey,
                pszValName,
                0,
                REG_DWORD,
                (LPBYTE)&dwValue,
                sizeof(DWORD));
}


//////////////////////////////////////////////////////////////////////////////
//
// RutlRegWriteString
//
//////////////////////////////////////////////////////////////////////////////
DWORD
RutlRegWriteString(
                    IN HKEY hKey,
                    IN PWCHAR pszValName,
                    IN PWCHAR pszValue
                  )
{
    return RegSetValueExW(
                hKey,
                pszValName,
                0,
                REG_SZ,
                (LPBYTE)pszValue,
                (wcslen(pszValue) + 1) * sizeof(WCHAR));
}


//////////////////////////////////////////////////////////////////////////////
//RutlParse
//
// Generic parse
//
//////////////////////////////////////////////////////////////////////////////
DWORD
RutlParse(
            IN  PWCHAR*         ppwcArguments,
            IN  DWORD           dwCurrentIndex,
            IN  DWORD           dwArgCount,
            IN  BOOL*           pbDone,
            OUT AAAAMON_CMD_ARG* pAaaaArgs,
            IN  DWORD           dwAaaaArgCount
         )
{
    DWORD            i, dwNumArgs, dwErr, dwLevel = 0;
    LPDWORD          pdwTagType = NULL;
    TAG_TYPE*        pTags = NULL;
    AAAAMON_CMD_ARG*  pArg = NULL;

    if ( !dwAaaaArgCount )
    {
        return ERROR_INVALID_PARAMETER;
    }

    do 
    {
        // Initialize
        dwNumArgs = dwArgCount - dwCurrentIndex;
        
        // Generate a list of the tags
        //
        pTags = (TAG_TYPE*)
            RutlAlloc(dwAaaaArgCount * sizeof(TAG_TYPE), TRUE);
        if ( !pTags )
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }
        for ( i = 0; i < dwAaaaArgCount; ++i )
        {
            CopyMemory(&pTags[i], &pAaaaArgs[i].rgTag, sizeof(TAG_TYPE));
        }
    
        // Get the list of present options
        //
        dwErr = RutlParseOptions(
                    ppwcArguments,
                    dwCurrentIndex,
                    dwArgCount,
                    dwNumArgs,
                    pTags,
                    dwAaaaArgCount,
                    &pdwTagType);
        if ( dwErr != NO_ERROR )
        {
            break;
        }

        // Copy the tag info back
        //
        for ( i = 0; i < dwAaaaArgCount; ++i )
        {
            CopyMemory(&pAaaaArgs[i].rgTag, &pTags[i], sizeof(TAG_TYPE));
        }
    
        for( i = 0; i < dwNumArgs; ++i )
        {
            // Validate the current argument
            //
            if ( pdwTagType[i] >= dwAaaaArgCount )
            {
                i = dwNumArgs;
                dwErr = ERROR_INVALID_SYNTAX;
                break;
            }
            pArg = &pAaaaArgs[pdwTagType[i]];

            // Get the value of the argument
            //
            switch ( pArg->dwType )
            {
                case AAAAMONTR_CMD_TYPE_STRING:
                {
                    pArg->Val.pszValue = 
                        RutlStrDup(ppwcArguments[i + dwCurrentIndex]);
                    break;
                }
                    
                case AAAAMONTR_CMD_TYPE_ENUM:
                {
                    dwErr = MatchEnumTag(g_hModule,
                                         ppwcArguments[i + dwCurrentIndex],
                                         pArg->dwEnumCount,
                                         pArg->rgEnums,
                                         &(pArg->Val.dwValue));

                    if(dwErr != NO_ERROR)
                    {
                        RutlDispTokenErrMsg(
                            g_hModule, 
                            EMSG_BAD_OPTION_VALUE,
                            pArg->rgTag.pwszTag,
                            ppwcArguments[i + dwCurrentIndex]);
                        i = dwNumArgs;
                        dwErr = ERROR_INVALID_PARAMETER;
                    }
                    break;
                }
            }
            if ( dwErr != NO_ERROR )
            {
                break;
            }

            // Mark the argument as present if needed
            //
            if ( pArg->rgTag.bPresent )
            {
                dwErr = ERROR_TAG_ALREADY_PRESENT;
                i = dwNumArgs;
                break;
            }
            pArg->rgTag.bPresent = TRUE;
        }
        if( dwErr != NO_ERROR )
        {
            break;
        }

        // Make sure that all of the required parameters have
        // been included.
        //
        for ( i = 0; i < dwAaaaArgCount; ++i )
        {
            if ( (pAaaaArgs[i].rgTag.dwRequired & NS_REQ_PRESENT)
             && !pAaaaArgs[i].rgTag.bPresent )
            {
                DisplayMessage(g_hModule, EMSG_CANT_FIND_EOPT);
                dwErr = ERROR_INVALID_SYNTAX;
                break;
            }
        }
        if ( dwErr != NO_ERROR )
        {
            break;
        }

    } while (FALSE);  
    
    // Cleanup
    {
        if ( pTags )
        {
            RutlFree(pTags);
        }
        if ( pdwTagType )
        {
            RutlFree(pdwTagType);
        }
    }

    return dwErr;
}

    
//////////////////////////////////////////////////////////////////////////////
// RefreshIASService
//
// Send a control 128 (refresh)to IAS
//
//////////////////////////////////////////////////////////////////////////////
HRESULT RefreshIASService()
{
    SC_HANDLE   hManager = OpenSCManager(
                                          NULL,   
                                          SERVICES_ACTIVE_DATABASE,
                                          SC_MANAGER_CONNECT |
                                          SC_MANAGER_ENUMERATE_SERVICE |
                                          SC_MANAGER_QUERY_LOCK_STATUS
                                        );
    if ( !hManager )
    {
        return E_FAIL;
    }


    SC_HANDLE hService = OpenServiceW(
                                         hManager,  
                                         L"IAS",
                                         SERVICE_USER_DEFINED_CONTROL | 
                                         SERVICE_QUERY_STATUS
                                     );
    
    if ( !hService )
    {
        CloseServiceHandle(hManager);
        return E_FAIL;
    }
 
    SERVICE_STATUS      ServiceStatus;
    BOOL    bResultOk = QueryServiceStatus(
                                            hService,
                                            &ServiceStatus  
                                          );
    HRESULT     hres;
    if ( bResultOk )
    {
        if ( (SERVICE_STOPPED == ServiceStatus.dwCurrentState) ||
             (SERVICE_STOP_PENDING == ServiceStatus.dwCurrentState))
        {
            //////////////////////////////////////////////////
            // service not running = nothing to do to refresh
            //////////////////////////////////////////////////
            hres = S_OK;
        }
        else 
        {
            /////////////////////////////////////////////////////
            // the service is running thus send the refresh code
            /////////////////////////////////////////////////////
            BOOL    bControlOk = ControlService(
                                                   hService,
                                                   128,
                                                   &ServiceStatus  
                                               );
            if ( bControlOk == FALSE )
            {
                hres = E_FAIL;
            }
            else
            {
                hres = S_OK;
            }
        }
    }
    else
    {
        hres = E_FAIL;
    }

    //////////
    // clean
    //////////
    CloseServiceHandle(hService);
    CloseServiceHandle(hManager);
    // hres is always defined here.
    return hres;
}