#include "precomp.h"

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x)   HeapFree(GetProcessHeap(), 0, (x))

DWORD
HandleShowAlias(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    return PrintAliasTable();
}

DWORD
HandleShellExit(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    BOOL bTmp;

    CallCommit(NETSH_COMMIT_STATE, &bTmp);

    if (!bTmp)
    {
        CallCommit(NETSH_FLUSH, &bTmp);
    }

    *pbDone = TRUE;

    return NO_ERROR;
}

DWORD
HandleShellLoad(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    DWORD dwErr, dwNumArgs;

    dwNumArgs = dwArgCount - dwCurrentIndex;

    //
    // Load Command
    //
    switch (dwNumArgs)
    {
        case 1 :
            return LoadScriptFile(ppwcArguments[dwCurrentIndex]);

        default :
            return ERROR_INVALID_SYNTAX;
    }

    return NO_ERROR;
}

DWORD
HandleShellSave(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    BOOL bTmp;

    CallCommit(NETSH_SAVE, &bTmp);
            
    return NO_ERROR;
}

DWORD
HandleShellUncommit(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    BOOL bTmp;

    CallCommit(NETSH_UNCOMMIT, &bTmp);
            
    return NO_ERROR;
}

DWORD
HandleSetMachine(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    TAG_TYPE pttTags[] = {{TOKEN_NAME, FALSE, FALSE }};
    DWORD    dwNumTags = sizeof(pttTags)/sizeof(TAG_TYPE);
    PDWORD   pdwTagType;
    DWORD    dwErr, dwNumArg, dwMode, i;
    BOOL     bTmp;

    dwNumArg = dwArgCount - dwCurrentIndex;

    if (dwNumArg < 1)
    {
        return SetMachine(NULL);
    }

    if (dwNumArg < 1)
    {
        return SetMachine(NULL);
    }

    if ((dwNumArg != 1) || IsHelpToken(ppwcArguments[dwCurrentIndex]))
    {
        return ERROR_SHOW_USAGE;
    }

    pdwTagType = MALLOC(dwNumArg * sizeof(DWORD));

    if (pdwTagType is NULL)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    dwErr = MatchTagsInCmdLine(g_hModule, 
                               ppwcArguments,
                               dwCurrentIndex,
                               dwArgCount,
                               pttTags,
                               dwNumTags,
                               pdwTagType);

    if (dwErr isnot NO_ERROR)
    {
        FREE(pdwTagType);
        if (dwErr is ERROR_INVALID_OPTION_TAG)
        {
            return ERROR_INVALID_SYNTAX;
        }
        return dwErr;
    }

    for ( i = 0; i < dwNumArg; i++)
    {
        switch (pdwTagType[i])
        {
            case 0: // NAME
            {
                SetMachine(ppwcArguments[i + dwCurrentIndex]);

                break;
            }

            default :
            {
                i = dwNumArg;

                dwErr = ERROR_INVALID_SYNTAX;

                break;
            }
        }
    }


    FREE(pdwTagType);

    switch(dwErr)
    {
        case NO_ERROR :
            break;

        case ERROR_TAG_ALREADY_PRESENT:
            PrintMessageFromModule(g_hModule, ERROR_TAG_ALREADY_PRESENT);
            return dwErr;

        default:
            return dwErr;
    }

    return dwErr;
}

extern HANDLE g_hLogFile;

DWORD
HandleSetFile(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    TAG_TYPE pttTags[] = {
				{TOKEN_MODE, TRUE, FALSE }, 
				{TOKEN_NAME, FALSE, FALSE }, 
				};
    DWORD    dwNumTags = sizeof(pttTags)/sizeof(TAG_TYPE);
    PDWORD   pdwTagType;
    DWORD    dwErr, dwNumArg, dwMode, i;
    BOOL     bTmp;
	LPCWSTR  wszFileName = NULL;
	HANDLE   hLogFile = NULL;

    dwNumArg = dwArgCount - dwCurrentIndex;

    if ((!dwNumArg) || (dwNumArg > 2) || IsHelpToken(ppwcArguments[dwCurrentIndex]))
    {
        return ERROR_SHOW_USAGE;
    }

    pdwTagType = MALLOC(dwNumArg * sizeof(DWORD));

    if (pdwTagType is NULL)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    dwErr = MatchTagsInCmdLine(g_hModule, 
                               ppwcArguments,
                               dwCurrentIndex,
                               dwArgCount,
                               pttTags,
                               dwNumTags,
                               pdwTagType);

    if (dwErr isnot NO_ERROR)
    {
        FREE(pdwTagType);
        if (dwErr is ERROR_INVALID_OPTION_TAG)
        {
            return ERROR_INVALID_SYNTAX;
        }
        return dwErr;
    }

    for ( i = 0; i < dwNumArg; i++)
    {
        switch (pdwTagType[i])
        {
            case 0: // Mode
            {
                TOKEN_VALUE    rgEnums[] = {{TOKEN_VALUE_OPEN,  0},
                                            {TOKEN_VALUE_APPEND, 1},
											{TOKEN_VALUE_CLOSE, 2}};

                dwErr = MatchEnumTag(g_hModule,
                                     ppwcArguments[i + dwCurrentIndex],
                                     sizeof(rgEnums)/sizeof(TOKEN_VALUE),
                                     rgEnums,
                                     &dwMode);

                if (dwErr != NO_ERROR)
                {
                    PrintMessageFromModule( g_hModule,
                                    ERROR_INVALID_OPTION_VALUE,
                                    pttTags[pdwTagType[i]].pwszTag,
                                    ppwcArguments[i + dwCurrentIndex]);

                    i = dwNumArg;

                     dwErr = ERROR_SHOW_USAGE;

                    break;
                }

                break;
            }
            case 1: // Name
            {
                wszFileName = ppwcArguments[i + dwCurrentIndex];
                break;
            }	
            default :
            {
                i = dwNumArg;

                dwErr = ERROR_INVALID_SYNTAX;

                break;
            }
        }
    }

    FREE(pdwTagType);
    switch(dwErr)
    {
        case NO_ERROR :
            break;

        case ERROR_TAG_ALREADY_PRESENT:
            PrintMessageFromModule(g_hModule, ERROR_TAG_ALREADY_PRESENT);
            return dwErr;

        default:
            return dwErr;
    }

    switch(dwMode) 
	{
    case 0: // open
		if (!wszFileName)
			return ERROR_SHOW_USAGE;

		if (g_hLogFile)
		{
			CloseHandle(g_hLogFile);
			g_hLogFile = NULL;
		}

		hLogFile = CreateFile(wszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if (INVALID_HANDLE_VALUE == hLogFile)
			return GetLastError();

		g_hLogFile = hLogFile;
        break;

	case 1: // append
		if (!wszFileName)
			return ERROR_SHOW_USAGE;

		if (g_hLogFile)
		{
			CloseHandle(g_hLogFile);
			g_hLogFile = NULL;
		}
	
		hLogFile = CreateFile(wszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if (INVALID_HANDLE_VALUE == hLogFile)
			return GetLastError();

		if (INVALID_SET_FILE_POINTER == SetFilePointer(hLogFile, 0, NULL, FILE_END))
			return GetLastError();
		
		g_hLogFile = hLogFile;

		break;

    case 2: // close
        if (wszFileName)
			return ERROR_SHOW_USAGE;

		if (g_hLogFile)
		{
			CloseHandle(g_hLogFile);
			g_hLogFile = NULL;
		}
        break;
    }

    return dwErr;
}


DWORD
HandleSetMode(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    TAG_TYPE pttTags[] = {{TOKEN_MODE, TRUE, FALSE }};
    DWORD    dwNumTags = sizeof(pttTags)/sizeof(TAG_TYPE);
    PDWORD   pdwTagType;
    DWORD    dwErr, dwNumArg, dwMode, i;
    BOOL     bTmp;

    dwNumArg = dwArgCount - dwCurrentIndex;

    if ((dwNumArg != 1) || IsHelpToken(ppwcArguments[dwCurrentIndex]))
    {
        return ERROR_SHOW_USAGE;
    }

    pdwTagType = MALLOC(dwNumArg * sizeof(DWORD));

    if (pdwTagType is NULL)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    dwErr = MatchTagsInCmdLine(g_hModule, 
                               ppwcArguments,
                               dwCurrentIndex,
                               dwArgCount,
                               pttTags,
                               dwNumTags,
                               pdwTagType);

    if (dwErr isnot NO_ERROR)
    {
        FREE(pdwTagType);
        if (dwErr is ERROR_INVALID_OPTION_TAG)
        {
            return ERROR_INVALID_SYNTAX;
        }
        return dwErr;
    }

    for ( i = 0; i < dwNumArg; i++)
    {
        switch (pdwTagType[i])
        {
            case 0: // LOGLEVEL
            {
                TOKEN_VALUE    rgEnums[] = {{TOKEN_VALUE_ONLINE,  TRUE},
                                            {TOKEN_VALUE_OFFLINE, FALSE}};

                dwErr = MatchEnumTag(g_hModule,
                                     ppwcArguments[i + dwCurrentIndex],
                                     sizeof(rgEnums)/sizeof(TOKEN_VALUE),
                                     rgEnums,
                                     &dwMode);

                if (dwErr != NO_ERROR)
                {
                    PrintMessageFromModule( g_hModule,
                                    ERROR_INVALID_OPTION_VALUE,
                                    pttTags[pdwTagType[i]].pwszTag,
                                    ppwcArguments[i + dwCurrentIndex]);

                    i = dwNumArg;

                    dwErr = ERROR_INVALID_PARAMETER;

                    break;
                }

                break;
            }

            default :
            {
                i = dwNumArg;

                dwErr = ERROR_INVALID_SYNTAX;

                break;
            }
        }
    }


    FREE(pdwTagType);

    switch(dwErr)
    {
        case NO_ERROR :
            break;

        case ERROR_TAG_ALREADY_PRESENT:
            PrintMessageFromModule(g_hModule, ERROR_TAG_ALREADY_PRESENT);
            return dwErr;

        default:
            return dwErr;
    }

    switch(dwMode) {
    case TRUE: // set to online
        dwErr = CallCommit(NETSH_COMMIT, &bTmp);
        break;

    case FALSE: // set to offline
        dwErr = CallCommit(NETSH_UNCOMMIT, &bTmp);
        break;
    }

    return dwErr;
}

DWORD
HandleShellCommit(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    BOOL bTmp;

    CallCommit(NETSH_COMMIT, &bTmp);
            
    return NO_ERROR;
}

DWORD
HandleShellFlush(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    BOOL bTmp;

    CallCommit(NETSH_FLUSH, &bTmp);
            
    return NO_ERROR;
}

DWORD
HandleShellUnalias(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    DWORD dwNumArgs;
    DWORD dwRes = NO_ERROR;

    dwNumArgs = dwArgCount - dwCurrentIndex;

    //
    // Unalias Command
    //

    switch (dwNumArgs)
    {
        case 1 :

            dwRes = ATDeleteAlias(ppwcArguments[dwCurrentIndex]);

            if (dwRes is NO_ERROR)
            {
                dwRes = ERROR_OKAY;
                break;
            }

            PrintMessageFromModule(g_hModule, MSG_ALIAS_NOT_FOUND, ppwcArguments[dwCurrentIndex]);

            break;

        default :

            dwRes = ERROR_INVALID_SYNTAX;
            break;

    }

    return dwRes;
}

DWORD
HandleShellAlias(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    LPWSTR         pwszAliasString;
    WCHAR          wszAliasString[MAX_CMD_LEN];
    DWORD          i, dwNumArgs, dwRes = NO_ERROR;

    dwNumArgs = dwArgCount - dwCurrentIndex;

    //
    // An alias command
    //
    switch (dwNumArgs)
    {
        case 0 : 
            //
            // Display all aliases in use
            //
            PrintAliasTable();
            
            break;

        case 1 :
            //
            // Display string for given alias
            //

            ATLookupAliasTable(ppwcArguments[dwCurrentIndex], &pwszAliasString);

            if (pwszAliasString)
            {
                PrintMessage(L"%1!s!\n",pwszAliasString);
            }
            else
            {
                PrintMessageFromModule( g_hModule, 
                                MSG_ALIAS_NOT_FOUND,
                                ppwcArguments[dwCurrentIndex] );
            }
            
            break;

        default :

            //
            // Set alias
            //

            if (IsLocalCommand(ppwcArguments[dwCurrentIndex], 0))
            {
                PrintMessageFromModule(g_hModule, EMSG_ALIASING_KEYWORD);
                break;
            }

            wszAliasString[0] = L'\0';
            
            for ( i = dwCurrentIndex+1 ; i < dwArgCount ; i++)
            {
                wcscat(wszAliasString, ppwcArguments[i]);
                wcscat(wszAliasString,L" ");
            }

            wszAliasString[wcslen(wszAliasString)-1] = L'\0';

            dwRes = ATAddAlias(ppwcArguments[dwCurrentIndex], wszAliasString);

            if (dwRes is NO_ERROR)
            {
                dwRes = ERROR_OKAY;
                break;
            }
                    
            //
            // Error in set alias
            //

            PrintMessageFromModule(g_hModule, MSG_CMD_FAILED);

            break;
    }

    return dwRes;
}

DWORD
HandleUbiqDump(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    DWORD                          dwErr = NO_ERROR;
    PNS_HELPER_TABLE_ENTRY         pHelper;

    //
    // Dump Command
    //

    do {
        dwErr = DumpContext( g_CurrentContext, 
                             ppwcArguments, 
                             dwArgCount, 
                             pvData);

    } while (FALSE);

    return dwErr;
}

DWORD
HandleUbiqHelp(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    DWORD dwDisplayFlags = CMD_FLAG_PRIVATE;

    if (g_bInteractive)
    {
        dwDisplayFlags |= CMD_FLAG_INTERACTIVE;
    }
    
    return DisplayContextHelp( g_CurrentContext,
                               dwDisplayFlags,
                               dwFlags,
                               dwArgCount-2+1,
                               NULL );
}

DWORD
HandleShowMode(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    BOOL bTmp;

    CallCommit(NETSH_COMMIT_STATE, &bTmp);

    if (bTmp)
    {
        PrintMessage(CMD_COMMIT);
    }
    else
    {
        PrintMessage(CMD_UNCOMMIT);
    }
    PrintMessage(MSG_NEWLINE);

    return NO_ERROR;
}

DWORD
HandleShellUplevel(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    DWORD       dwRes;
    PLIST_ENTRY pleHead, ple;
    PARG_ENTRY  pae;

    // Convert current context to list
    dwRes = ConvertBufferToArgList(&pleHead, g_pwszContext);
    if (dwRes isnot NO_ERROR) 
    {
        return dwRes;
    }

    // Remove last element if more than two
    if (!IsListEmpty(pleHead) and (pleHead->Flink->Flink isnot pleHead))
    {
        // Delete the last element of the context list
        // (Try inheriting a command from one level up)

        ple = pleHead->Blink;
        pae = CONTAINING_RECORD(ple, ARG_ENTRY, le);
        if (pae->pwszArg)
            FREE(pae->pwszArg);
        RemoveEntryList(ple);
        FREE(pae);
    }

    // Convert back to buffer
    dwRes = ConvertArgListToBuffer(pleHead, g_pwszContext);

    FREE_ARG_LIST(pleHead);

    return NO_ERROR;
}

typedef struct {
    LIST_ENTRY le;
    WCHAR      wszBuffer[MAX_CMD_LEN];
} CONTEXT_BUFFER, *PCONTEXT_BUFFER;

LIST_ENTRY leContextStackHead;
BOOL bContextStackInit = FALSE;

VOID
InitContextStack()
{
    if (bContextStackInit)
        return;

    bContextStackInit = TRUE;

    InitializeListHead(&leContextStackHead);
}

DWORD
HandleShellPushd(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    PCONTEXT_BUFFER pcb;
    DWORD           dwErr = NO_ERROR;

    InitContextStack();

    // Malloc another buffer
    pcb = MALLOC(sizeof(CONTEXT_BUFFER));
    if (!pcb)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    wcscpy(pcb->wszBuffer, g_pwszContext);

    // Push buffer on stack
    InsertHeadList(&leContextStackHead, &pcb->le);

    if (dwArgCount > dwCurrentIndex)
    {
        LPWSTR pwszBuffer;

        // execute the rest of the arguments as a new command

        // Copy arg array to a buffer
        ConvertArgArrayToBuffer( dwArgCount - dwCurrentIndex, 
                                 ppwcArguments + dwCurrentIndex,
                                 &pwszBuffer );

        if (!pwszBuffer)
        {
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        dwErr = ProcessCommand(pwszBuffer, pbDone);
        if (dwErr)
        {
            dwErr = ERROR_SUPPRESS_OUTPUT;
        }
        FREE(pwszBuffer);

        // XXX If the command failed, we probably want to set the
        // XXX current context to some NULL context so all commands fail
    }

    return dwErr;
}

DWORD
HandleShellPopd(
    LPCWSTR   pwszMachine,
    LPWSTR   *ppwcArguments,
    DWORD     dwCurrentIndex,
    DWORD     dwArgCount,
    DWORD     dwFlags,
    LPCVOID   pvData,
    BOOL     *pbDone
    )
{
    PLIST_ENTRY ple;
    PCONTEXT_BUFFER pcb;

    InitContextStack();

    if (IsListEmpty(&leContextStackHead))
        return NO_ERROR;

    // Pop buffer off stack
    ple = leContextStackHead.Flink;
    pcb = CONTAINING_RECORD(ple, CONTEXT_BUFFER, le);
    RemoveEntryList(ple);

    // Copy buffer to current context
    wcscpy( g_pwszContext, pcb->wszBuffer );

    // Free buffer
    FREE(pcb);

    return NO_ERROR;
}