/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    client.c

Abstract:

    This sample illustrates a simple client side of the DuplicateTokenEx()
    server sample.

    This client functions correctly on the following platforms:
    Windows NT 3.5
    Windows NT 3.51
    Windows NT 4.0
    Windows 95

Author:

    Scott Field (sfield)    02-Apr-96

--*/

#include <windows.h>
#include <stdio.h>

DWORD
WINAPI
ReadFunc(
    LPVOID lpParam
    );

DWORD
WINAPI
WriteFunc(
    LPVOID lpParam
    );

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    );

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

int
__cdecl
main(
    int argc,
    char *argv[]
    )
{
    HANDLE hFileRead;
    HANDLE hFileWrite;

    HANDLE hThread;
    DWORD dwThreadId;

    CHAR PipeIn[255];
    CHAR PipeOut[255];

    if(argc == 1) {
        printf("Usage: %s <\\\\server\n", argv[0]);
        printf("  %s \\\\WINBASE (remote server is \\\\winbase)\n", argv[0]);
        printf("  %s \\\\. (local server)\n",   argv[0]);
        return RTN_USAGE;
    }

    //
    // build Pipe names
    //
    wsprintf(PipeIn, "%s\\pipe\\rcmd_out", argv[1]);
    wsprintf(PipeOut, "%s\\pipe\\rcmd_in", argv[1]);

    //
    // wait for the outbound (inbound on server-side) pipe to be available
    //
    WaitNamedPipe(PipeOut, NMPWAIT_WAIT_FOREVER);

    hFileWrite = CreateFile(
        PipeOut,
        GENERIC_WRITE,
        0, // exclusive open
        NULL,
        OPEN_EXISTING,
        SECURITY_SQOS_PRESENT | SECURITY_IMPERSONATION, // server can impersonate
        NULL
        );

    if(hFileWrite == INVALID_HANDLE_VALUE) {
        DisplayWinError("CreateFile", GetLastError());
        return RTN_ERROR;
    }

    //
    // wait for the inbound (outbound on server-side) pipe to be available
    // we time-out in 5 seconds because the server should be ready for us
    // right-away
    //
    WaitNamedPipe(PipeIn, 5 * 1000);

    hFileRead = CreateFile(
        PipeIn,
        GENERIC_READ,
        0, // exclusive open
        NULL,
        OPEN_EXISTING,
        0,
        NULL
        );

    if(hFileRead == INVALID_HANDLE_VALUE) {
        DisplayWinError("CreateFile", GetLastError());
        return RTN_ERROR;
    }

    //
    // start a thread to handle stdin redirection to the pipe
    //
    hThread = CreateThread(
        NULL,
        0,
        WriteFunc,
        (LPVOID)hFileWrite, // parm
        0,
        &dwThreadId
        );

    if(hThread == NULL) {
        DisplayWinError("CreateThread", GetLastError());
        return RTN_ERROR;
    }

    //
    // handle stdout redirection
    //
    ReadFunc((LPVOID)hFileRead);

    //
    // if the read returned, kill the write, cleanup, and then exit
    //
    TerminateThread(hThread, 0xFFFFFFFF);
    CloseHandle(hThread);

    CloseHandle(hFileRead);
    CloseHandle(hFileWrite);

    return RTN_OK;
}

DWORD
WINAPI
ReadFunc(
    LPVOID lpParam
    )
{
    #define BUFFER_SIZE 4096

    HANDLE hFile = (HANDLE)lpParam;
    BYTE lpBuffer[BUFFER_SIZE];

    DWORD dwBytesRead;
    DWORD dwBytesWritten;

    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    DWORD dwLastError;

    while (1) {
        if(!ReadFile(
            hFile,
            lpBuffer,
            BUFFER_SIZE,
            &dwBytesRead,
            NULL
            )) {
            dwLastError = GetLastError();

            if(dwLastError == ERROR_NO_DATA) continue;
            if(dwLastError != ERROR_MORE_DATA) break;
        }

        if(!WriteFile(
            hStdOut,
            lpBuffer,
            dwBytesRead,
            &dwBytesWritten,
            NULL
            )) break;
    }

    return GetLastError();
}

DWORD
WINAPI
WriteFunc(
    LPVOID lpParam
    )
{
    HANDLE hPipe = (HANDLE)lpParam;

    BYTE lpBuffer[ 1 ]; // change this later

    DWORD dwBytesRead;
    DWORD dwBytesWritten;

    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);

    while (1) {
        //
        // TODO rework this loop to be line-based.
        // this would entail doing the console buffering outselves.
        //
        if(!ReadFile(
            hStdIn,
            lpBuffer,
            1,
            &dwBytesRead,
            NULL
            )) break;

        if(!WriteFile(
            hPipe,
            lpBuffer,
            dwBytesRead,
            &dwBytesWritten,
            NULL
            )) break;
    }

    return GetLastError();
}

void
DisplayWinError(
    LPSTR szAPI,    // pointer to Ansi function name
    DWORD dwError   // DWORD WinError
    )
{
    LPSTR MessageBuffer;
    DWORD dwBufferLength;

    //
    // TODO get this fprintf out of here!
    //
    fprintf(stderr,"%s error!\n", szAPI);

    if(dwBufferLength=FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR) &MessageBuffer,
            0,
            NULL
            ))
    {
        DWORD dwBytesWritten; // unused

        //
        // Output message string on stderr
        //
        WriteFile(
                GetStdHandle(STD_ERROR_HANDLE),
                MessageBuffer,
                dwBufferLength,
                &dwBytesWritten,
                NULL
                );

        //
        // free the buffer allocated by the system
        //
        LocalFree(MessageBuffer);
    }
}
