SERVER.C
/* 
Copyright (c) 1994, 1993 Microsoft Corporation 
 
Module Name: 
    Server.c 
 
Abstract: 
    The server component of Remote. It spawns a child process 
    and redirects the stdin/stdout/stderr of child to itself. 
    Waits for connections from clients - passing the 
    output of child process to client and the input from clients 
    to child process. 
 
Author: 
    Rajivendra Nath (rajnath) 2-Jan-1993 
 
Environment: 
    Console App. User mode. 
 
Revision History: 
Alex Wetmore (t-alexwe) 6-Jun-1994 
- converted remote to use APPC with Windows SNA Server instead of named 
  pipes 
- converted to use Unicode 
*/ 
 
#include <windows.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <io.h> 
#include <string.h> 
#include <winappc.h> 
#include "appclib.h" 
#include "aremote.h" 
 
#define COMMANDFORMAT       TEXT("%c%-15s    [%-15s %d:%d]\n%c") 
#define RemoteInfo(prt,flg) {if (!(flg&0x80000000)) prt;} 
 
#define CMDSTRING(OutBuff,InpBuff,Client,sTime) \ 
        {                                \ 
            /* int xxlen; */             \ 
            wsprintf                      \ 
            (                            \ 
               &OutBuff[0],COMMANDFORMAT,\ 
               BEGINMARK,InpBuff,        \ 
               Client->Name,sTime.wHour, \ 
               sTime.wMinute,ENDMARK     \ 
            );                           \ 
         } 
 
#define BUFFSIZE      1024 
 
SESSION_TYPE ClientList[MAX_SESSION]; 
 
HANDLE  ChildStdInp;     //Server Writes to  it 
HANDLE  ChildStdOut;     //Server Reads from it 
HANDLE  ChildStdErr;     //Server Reads from it 
 
HANDLE  SaveFile;       //File containing all that was 
                        //output by child process. 
                        //Each connection opens a handle to this file 
                        //and is sent through PipeWriteH. 
 
TCHAR   SaveFileName[64]; //Name of above file - all new sessions need 
HANDLE  ChldProc;         //Handle to the Child Process 
HANDLE  ListenThreadH;    //Handle to the thread listening for connections 
                          //from Remote Clients. 
extern BOOL AutoStarted;// true if this is an autostarted service 
 
CRITICAL_SECTIONclose_crit;// critical section for closing 
 
HANDLE 
ForkChildProcess(          // Creates a new process 
    TCHAR *cmd,             // Redirects its stdin,stdout 
    PHANDLE in,            // and stderr - returns the 
    PHANDLE out,           // corresponding pipe ends. Using currently 
    PHANDLE err 
    ); 
 
DWORD WINAPI 
ListenForSession(          //THREAD:Listens for new connections and 
    TCHAR* pipe             //spawns of new seesions - Updates the 
    );                     //Status in Client DataStructure. Seperate Thread. 
 
// 
// threads for dealing with local session (only when non-autostarted) 
DWORD WINAPI LocalSession(SESSION_TYPE *cli); 
DWORD WINAPI StdoutThread(SESSION_TYPE *cli); 
DWORD WINAPI StdinThread(SESSION_TYPE *cli); 
 
BOOL 
CreateMySecurityDescriptor(                    // 
    PSECURITY_DESCRIPTOR pSecurityDescriptor,  // Creates a security descriptor 
    TCHAR *Owner                                // with discretionary access for 
    );                                         // access for Owner. 
 
 
DWORD 
NewSession(                //Manages the session with a client. 
    SESSION_TYPE* Client 
    ); 
 
DWORD WINAPI               //2 THREAD:Each reads either 
GetChldOutput(             //StdOut or StdErr of child and 
    HANDLE rhandle         //writes to SaveFile. Seperate Thread. 
    ); 
 
DWORD WINAPI 
TransferFileToClient(      //X THREADS:Reads the save 
    SESSION_TYPE* Client   //file and sendsoutput to a client. Seperate Thread 
    ); 
 
 
DWORD WINAPI 
GetClientInput(            //Times X THREADS:Gets input from Child pipe 
    SESSION_TYPE* Client   //and sends to childs StdIn. Seperate Thread. 
    ); 
 
 
BOOL 
FilterCommand(             //Filters input from client 
    SESSION_TYPE *cl,      //for commands intended for REMOTE 
    TCHAR *buff, 
    int dread 
    ); 
 
DWORD WINAPI               // Manages the IO with Remote Client. 
RemoteSession( 
    SESSION_TYPE* Client 
    ); 
 
BOOL                       // Ctrl-C handler 
SrvCtrlHand( 
    DWORD event 
    ); 
 
VOID                       // @s command to remote 
SendStatus( 
    tpconvid_t tpconv 
    ); 
 
DWORD WINAPI               // @p command to remote 
ShowPopup( 
    TCHAR *mssg 
    ); 
 
VOID                       // Removes the command begin and end markers 
RemoveInpMark(             // from the save file. 
    char* Buff, 
    DWORD Size 
    ); 
 
VOID                       // Cleans up the session 
CloseClient(               // once it ends. 
    SESSION_TYPE *Client 
    ); 
                           // Initialises the Client datastructs 
VOID 
InitClientList( 
    ); 
 
 
/*************************************************************/ 
/* The main entry point for the Server End of Remote         */ 
/*************************************************************/ 
VOID Server(TCHAR* ChildCmd, TCHAR *tp_name) { 
    DWORD  ThreadID ; 
    HANDLE WaitH[3], objs[MAX_SESSION]; 
    DWORD  WaitObj; 
    TCHAR  tmpdir[32]; 
int    i, active; 
 
    WRITEF((VBuff, TEXT("**************************************\n"))); 
    WRITEF((VBuff, TEXT("***********     REMOTE    ************\n"))); 
    WRITEF((VBuff, TEXT("***********     SERVER    ************\n"))); 
    WRITEF((VBuff, TEXT("**************************************\n"))); 
 
    InitClientList(); 
 
appcinit(); 
 
// 
// This critical section is used around close so that only one appcclose 
// is happening at a time. 
// 
InitializeCriticalSection(&close_crit); 
 
    // 
    //Start the command as a child process 
    // 
 
    ChldProc=ForkChildProcess(ChildCmd,&ChildStdInp,&ChildStdOut,&ChildStdErr); 
 
    // 
    //Create a tempfile for storing Child process output. 
    // 
    { 
         DWORD size=sizeof(tmpdir); 
         if ( 
             (GetEnvironmentVariable(TEXT("TMP"),tmpdir,size)==0)&& 
             (GetEnvironmentVariable(TEXT("TEMP"),tmpdir,size)==0) 
            ) 
         { 
            wsprintf(tmpdir,TEXT("%ws"),TEXT(".")); 
         } 
         if (!GetTempFileName(tmpdir,TEXT("REMOTE"),0,SaveFileName)) 
            GetTempFileName(TEXT("."),TEXT("REMOTE"),0,SaveFileName); 
    } 
 
 
    if ((SaveFile=CreateFile 
                  ( 
                     (LPCTSTR)SaveFileName,           /* address of name of the file  */ 
                     GENERIC_READ|GENERIC_WRITE,      /* access (read/write) mode */ 
                     FILE_SHARE_READ|FILE_SHARE_WRITE,/* share mode   */ 
                     (LPSECURITY_ATTRIBUTES)NULL,     /* security descriptor  */ 
                     CREATE_ALWAYS,                   /* how to create    */ 
                     FILE_ATTRIBUTE_NORMAL,           /* File Attribute */ 
                     (HANDLE)NULL) 
                  )==NULL) 
    { 
        TerminateProcess(ChldProc,0); 
        ErrorExit(TEXT("Could not Create Output File")); 
    } 
 
 
    // 
    //Start 2 threads to save the output from stdout and stderr of cmd to savefile. 
    // 
 
    if ((WaitH[0]=CreateThread 
                  ( 
                     (LPSECURITY_ATTRIBUTES)NULL,           // No security attributes. 
                     (DWORD)0,                              // Use same stack size. 
                     (LPTHREAD_START_ROUTINE)GetChldOutput, // Thread procedure. 
                     (LPVOID)ChildStdErr,                   // Parameter to pass. 
                     (DWORD)0,                              // Run immediately. 
                     (LPDWORD)&ThreadID) 
                   )==NULL) 
    { 
 
        TerminateProcess(ChldProc,0); 
        ErrorExit(TEXT("Failed to Create GetGhldOutput#1 Thread")); 
    } 
 
 
    if ((WaitH[1]=CreateThread 
                  ( 
                     (LPSECURITY_ATTRIBUTES)NULL,           // No security attributes. 
                     (DWORD)0,                              // Use same stack size. 
                     (LPTHREAD_START_ROUTINE)GetChldOutput, // Thread procedure. 
                     (LPVOID)ChildStdOut,                   // Parameter to pass. 
                     (DWORD)0,                              // Run immediately. 
                     (LPDWORD)&ThreadID) 
                  )==NULL) 
    { 
 
        TerminateProcess(ChldProc,0); 
        ErrorExit(TEXT("Failed to Create GetGhldOutput#2 Thread")); 
    } 
 
 
    // 
    // Start Thread for local session 
// only run if this is not an autostarted TP 
    // 
if (!AutoStarted) { 
ClientList[0].Active = TRUE; 
   if ((ClientList[0].hThread = CreateThread( 
      (LPSECURITY_ATTRIBUTES) NULL,   // No security attributes. 
      (DWORD) 0,                      // Use same stack size. 
      (LPTHREAD_START_ROUTINE) LocalSession, // Thread procedure. 
      (LPVOID) &ClientList[0],            // Parameter to pass. 
      (DWORD) 0,                          // Run immediately. 
      (LPDWORD) &ThreadID))==NULL) { 
       TerminateProcess(ChldProc,0); 
       ErrorExit(TEXT("Failed To Create LocalSession Thread")); 
   } 
} 
 
    // 
    //Start Thread to listen for new Connections 
    // 
 
    if ((ListenThreadH=CreateThread 
                       ( 
                         (LPSECURITY_ATTRIBUTES)NULL,        // No security attributes. 
                         (DWORD)0,                           // Use same stack size. 
                         (LPTHREAD_START_ROUTINE)ListenForSession, // Thread procedure. 
                         (LPVOID)tp_name,                    // Parameter to pass. 
                         (DWORD)0,                           // Run immediately. 
                         (LPDWORD)&ThreadID) 
                       )==NULL) 
    { 
 
        TerminateProcess(ChldProc,0); 
        ErrorExit(TEXT("Failed To Create ListenForSession Thread")); 
 
    } 
 
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)SrvCtrlHand,TRUE); 
 
    WaitH[2]=ChldProc; 
 
    // 
    // Wait until the child process terminates 
    // or local IO thread terminates 
    // or IO with child process ends 
    // 
 
    WaitObj=WaitForMultipleObjects(3,WaitH,FALSE,INFINITE); 
 
    switch (WaitObj-WAIT_OBJECT_0) 
    { 
        case 0:      // Error Writing to savefile 
        case 1: 
            TerminateProcess(ChldProc,0); 
            break; 
        case 2:      // Child Proc Terminated 
            break; 
 
        default:     // Out of Some Resource 
            WRITEF((VBuff, 
TEXT("Out of Resource Error %d..Terminating\n"), 
GetLastError())); 
            break; 
 
    } 
 
    TerminateThread(ListenThreadH,0); 
 
    CloseHandle(ChildStdInp); 
    CloseHandle(ChildStdOut); 
    CloseHandle(ChildStdErr); 
 
    WRITEF((VBuff, 
TEXT("\nServer: Child process \"%ws\" died, parent exiting...\n"), 
ChildCmd)); 
 
    CloseHandle(SaveFile); 
 
// signal all of the client threads to tell them that they should die 
for (i = 0, active = 0; i < MAX_SESSION; i++) { 
if (ClientList[i].Active) { 
SetEvent(ClientList[i].DoClose); 
objs[active++] = ClientList[i].hThread; 
} 
} 
 
// Wait for all of the client to die gracefully 
WaitForMultipleObjects(active, objs, TRUE, INFINITE); 
 
    for (i = 0; i < MAX_SESSION; i++) { 
    if (ClientList[i].rSaveFile != INVALID_HANDLE_VALUE) 
        CloseHandle(ClientList[i].rSaveFile); 
 
    if (ClientList[i].MoreData != NULL) 
        CloseHandle(ClientList[i].MoreData); 
} 
 
    if (!DeleteFile(SaveFileName)) 
          WRITEF((VBuff,TEXT("Temp File %ws not deleted..\n"),SaveFileName)); 
 
appcdestroy(); 
 
    return; 
} 
 
/*************************************************************/ 
/* Creates the child process and redirects its std.IO handles*/ 
/*************************************************************/ 
// these handles should be treated as if they send and receive ANSI, not Unicode 
HANDLE 
ForkChildProcess(          // Creates a new process 
    TCHAR *cmd,             // Redirects its stdin,stdout 
    PHANDLE inH,           // and stderr - returns the 
    PHANDLE outH,          // corresponding pipe ends. 
    PHANDLE errH 
    ) 
{ 
    SECURITY_ATTRIBUTES lsa; 
    STARTUPINFO         si; 
    PROCESS_INFORMATION pi; 
 
    HANDLE ChildIn; 
    HANDLE ChildOut; 
    HANDLE ChildErr; 
 
    lsa.nLength=sizeof(SECURITY_ATTRIBUTES); 
    lsa.lpSecurityDescriptor=NULL; 
    lsa.bInheritHandle=TRUE; 
 
    // 
    //Create Parent_Write to ChildStdIn Pipe 
    // 
 
    if (!CreatePipe(&ChildIn,inH,&lsa,0)) 
        ErrorExit(TEXT("Could Not Create Parent-->Child Pipe")); 
 
    // 
    //Create ChildStdOut to Parent_Read pipe 
    // 
 
    if (!CreatePipe(outH,&ChildOut,&lsa,0)) 
        ErrorExit(TEXT("Could Not Create Child-->Parent Pipe")); 
 
    // 
    //Create ChildStdOut to Parent_Read pipe 
    // 
 
    if (!CreatePipe(errH,&ChildErr,&lsa,0)) 
        ErrorExit(TEXT("Could Not Create Child-->Parent Pipe")); 
 
    // 
    // Lets Redirect Console StdHandles - easy enough 
    // 
 
 
    si.cb=sizeof(STARTUPINFO); 
    si.lpReserved=NULL; 
    si.lpTitle=NULL; 
    si.lpDesktop=NULL; 
    si.dwX=si.dwY=si.dwYSize=si.dwXSize=0; 
    si.dwFlags=STARTF_USESTDHANDLES; 
    si.hStdInput =ChildIn; 
    si.hStdOutput=ChildOut; 
    si.hStdError =ChildErr; 
    si.wShowWindow=SW_SHOW; 
    si.lpReserved2=NULL; 
    si.cbReserved2=0; 
 
    // 
    //Create Child Process 
    // 
 
    if (!CreateProcess 
         ( 
            NULL, 
            cmd, 
            NULL, 
            NULL, 
            TRUE, 
            NORMAL_PRIORITY_CLASS, 
            NULL, 
            NULL, 
            &si, 
            &pi) 
         ) 
    { 
        if (GetLastError()==2) 
            WRITEF((VBuff,TEXT("Executable %ws not found\n"),cmd)); 
        ErrorExit(TEXT("Could Not Create Child Process")); 
    } 
 
    // 
    //Close unneccesary Handles and Restore the crt handles 
    // 
 
    CloseHandle(ChildIn); 
    CloseHandle(ChildOut); 
    CloseHandle(ChildErr); 
 
    return(pi.hProcess); 
} 
 
/*************************************************************/ 
/* Listens for sessions from Clients and creates a new thread*/ 
/* for each client                                           */ 
/*************************************************************/ 
 
DWORD WINAPI ListenForSession(TCHAR* tp_name) { 
    int    i; 
    DWORD  ThreadID; 
tpconvid_t tpconv; 
    HANDLE TokenHandle; 
    TOKEN_DEFAULT_DACL DefaultDacl; 
 
WRITEF((VBuff, TEXT("\nServer: Waiting for clients on TP: %ws\n\n"), tp_name)); 
 
    DefaultDacl.DefaultDacl = NULL; 
 
    if (OpenProcessToken 
        ( 
            GetCurrentProcess(), 
            TOKEN_ADJUST_DEFAULT, 
            &TokenHandle 
        )) 
    { 
 
        // 
        // Remove the default DACL on the token 
        // 
 
        SetTokenInformation 
        ( 
            TokenHandle, 
            TokenDefaultDacl, 
            &DefaultDacl, 
            sizeof( TOKEN_DEFAULT_DACL ) 
        ); 
 
    } 
 
    while(TRUE) { 
tpconv = appclisten(tp_name); 
 
        // 
        // Look For a Free Slot 
// There is guaranteed to be one or appclisten wouldn't have run. 
        // 
 
        for (i = 0; i < MAX_SESSION; i++) { 
            // 
            // Locate a Free Client block 
            // 
            if (!ClientList[i].Active) break; 
        } 
 
// check to see if we couldn't find a free session 
if (i == MAX_SESSION) { 
WRITEF((VBuff, TEXT("\nServer: Client tried to connect with all sessions in use\n"))); 
appcclose(tpconv); 
continue; 
} else { 
appcwrite(tpconv, REMOTE_INIT_MSG, 
lstrlen(REMOTE_INIT_MSG) * sizeof(TCHAR)); 
} 
 
        // 
        // Initialize the Client 
        // 
        ClientList[i].tpconv = tpconv; 
        ClientList[i].Active = TRUE; 
        ClientList[i].SendOutput = TRUE; 
        ClientList[i].CommandRcvd = FALSE; 
 
        // 
        //start new thread for this connection 
        // 
 
        if((ClientList[i].hThread=CreateThread 
            ( 
                 (LPSECURITY_ATTRIBUTES)NULL,        // No security attributes. 
                 (DWORD)0,                           // Use same stack size. 
                 (LPTHREAD_START_ROUTINE)RemoteSession, // Thread procedure. 
                 (LPVOID)&ClientList[i],             // Parameter to pass. 
                 (DWORD)0,                           // Run immediately. 
                 (LPDWORD)&ThreadID) 
            )==NULL) 
        { 
            CloseClient(&ClientList[i]); 
            continue; 
        } 
 
    } 
    return(0); 
} 
 
/*************************************************************/ 
/* Creates a security descriptor with the discrtionry access */ 
/* for the account specified in the /U switch  if any        */ 
/*************************************************************/ 
 
BOOL 
CreateMySecurityDescriptor( 
    PSECURITY_DESCRIPTOR pSecurityDescriptor, 
    TCHAR *Owner 
    ) 
{ 
    PSID pOwnerSid; 
    PACL pAcl; 
    BOOL Ret=FALSE; 
 
    // 
    // Initialize the Security Descriptor struct. 
    // 
 
 
    InitializeSecurityDescriptor 
    ( 
        pSecurityDescriptor, 
        SECURITY_DESCRIPTOR_REVISION 
    ); 
 
    if (Owner==NULL) 
    { 
        // 
        // No security required. 
        // 
 
        SetSecurityDescriptorDacl 
        ( 
               pSecurityDescriptor, 
               TRUE, 
               NULL, 
               FALSE 
        ); 
 
        return TRUE; 
    } 
 
    { 
        // 
        // Get the SID for the account/Group 
        // 
 
        DWORD len1=1024,len2=1024; 
        TCHAR RefDomain[1024]; 
        SID_NAME_USE snu=0;        //don't care 
 
        if ((pOwnerSid=(PSID)LocalAlloc(LMEM_FIXED, len1))==NULL) 
            return FALSE; 
 
 
        Ret= 
        LookupAccountName 
        ( 
            NULL, 
            Owner, 
            pOwnerSid, 
            &len1, 
            RefDomain, 
            &len2, 
            &snu 
        ); 
 
        if (!Ret) 
        { 
            LocalFree(pOwnerSid); 
            return FALSE; 
        } 
 
    } 
 
    { 
 
        // 
        // Create the access control list with access for 
        // the SID obtained above. 
        // 
 
        DWORD aclsize=sizeof(ACL)+ 
                      sizeof(ACCESS_ALLOWED_ACE)+ 
                      GetLengthSid(pOwnerSid)- 
                      sizeof(DWORD); 
 
        if ((pAcl=(PACL)LocalAlloc(LMEM_FIXED,aclsize))==NULL) 
        { 
            LocalFree(pOwnerSid); 
            return FALSE; 
        } 
 
        // 
        // Initialize the acl buffer 
        // 
 
        Ret= 
        InitializeAcl 
        ( 
            pAcl, 
            aclsize, 
            ACL_REVISION 
        ); 
 
        if (!Ret) 
        { 
            LocalFree(pOwnerSid); 
            LocalFree(pAcl); 
            return FALSE; 
        } 
 
        // 
        // Add the sid to the access allowed part in ACL 
        // 
 
        Ret= 
        AddAccessAllowedAce 
        ( 
            pAcl, 
            ACL_REVISION, 
            GENERIC_ALL, 
            pOwnerSid 
        ); 
 
        if (!Ret) 
        { 
            LocalFree(pOwnerSid); 
            LocalFree(pAcl); 
            return FALSE; 
        } 
    } 
 
    // 
    // Add the created ACL to the discreationary control list 
    // 
 
    Ret= 
    SetSecurityDescriptorDacl 
    ( 
        pSecurityDescriptor, 
        TRUE, 
        pAcl, 
        FALSE 
    ); 
 
    if (!Ret) 
    { 
        LocalFree(pOwnerSid); 
        LocalFree(pAcl); 
        return FALSE; 
    } 
    return TRUE; 
} 
 
/*************************************************************/ 
/* Manages the Session with a Client - Creates a thread for  */ 
/* Inputs from the client and a thread for sending outputs to*/ 
/* the client. Could have just as easily done with 1 thread  */ 
/* using Asyn IO.                                            */ 
/*************************************************************/ 
DWORD WINAPI RemoteSession(SESSION_TYPE         *MyClient) { 
    SESSION_STARTUPINFO  ssi; 
    DWORD                tmp; 
    SESSION_STARTREPLY   ssr; 
    SYSTEMTIME           st; 
 
    GetLocalTime(&st); 
    memset((TCHAR *)&ssi,0,sizeof(ssi)); 
 
    // 
    // Open a new handle to the save file ... 
    // contains the saved output from the child process 
    // and the commands already given to it. 
    // 
 
    if ((MyClient->rSaveFile=CreateFile 
        ( 
            SaveFileName, 
            GENERIC_READ|GENERIC_WRITE, 
            FILE_SHARE_READ|FILE_SHARE_WRITE, 
            NULL,OPEN_EXISTING, 
            FILE_ATTRIBUTE_NORMAL,NULL) 
        )==NULL) 
 
    { 
        CloseClient(MyClient); 
        return(1); 
    } 
 
    // 
    // Exchange Remote Information with Client. 
    // 
appcread(MyClient->tpconv, (void *) &ssi, sizeof(ssi)); 
wcscpy(MyClient->Name, ssi.ClientName); 
MyClient->Name[15] = 0; 
 
ssr.FileSize = GetFileSize(MyClient->rSaveFile, &tmp); 
ssr.MagicNumber = MAGICNUMBER; 
 
appcwrite(MyClient->tpconv, (void *) &ssr, sizeof(ssr)); 
 
    /* Lines  */ 
    if (ssi.LinesToSend!=-1) 
    { 
        long  PosFromEnd=ssi.LinesToSend*CHARS_PER_LINE; 
        DWORD BytesToSend=MINIMUM((DWORD)PosFromEnd,ssr.FileSize); 
        DWORD BytesRead, usize; 
        char *abuff=(char *)LocalAlloc(LMEM_FIXED,(BytesToSend+1)*sizeof(char)); 
        TCHAR *ubuff=(TCHAR *)LocalAlloc(0, (BytesToSend+1)*sizeof(TCHAR)); 
 
        if (ssr.FileSize > (DWORD)PosFromEnd) 
        { 
            SetFilePointer( 
                            MyClient->rSaveFile, 
                            -PosFromEnd, 
                            (PLONG)NULL, 
                            FILE_END 
                          ); 
        } 
 
        if (abuff!=NULL && ubuff!=NULL) 
        { 
            if (!ReadFile(MyClient->rSaveFile,abuff,BytesToSend,&BytesRead,NULL)) 
            { 
                CloseClient(MyClient); 
                return(1); 
            } 
 
            // Don't want the markers to be part of the output display 
            // at the client end. 
            RemoveInpMark(abuff, BytesRead); 
 
// convert to wbs 
usize = mbstowcs(ubuff, abuff, BytesRead); 
 
appcwrite(MyClient->tpconv, (void *) ubuff, usize * sizeof(TCHAR)); 
        } 
        LocalFree(abuff); 
LocalFree(ubuff); 
 
    } 
 
    RemoteInfo(WRITEF((VBuff, TEXT("\nServer: Connected To %ws [%02d:%02d]\n"),MyClient->Name,st.wHour,st.wMinute)),ssi.Flag); 
 
    // 
    // Start off the new session. 
    // 
    NewSession(MyClient); 
 
    RemoteInfo(WRITEF((VBuff, TEXT("\nServer: Disconnected From %ws [%02d:%02d]\n"),MyClient->Name,st.wHour,st.wMinute)),ssi.Flag); 
 
LocalFree(MyClient->tpconv); 
 
    return(0); 
} 
 
// 
// LocalSession: creates a session for use by the local terminal 
// 
// argument is ignored 
// 
DWORD WINAPI LocalSession(SESSION_TYPE *cli) { 
BOOLdone = FALSE; 
HANDLErthread, wthread; 
DWORDtid; 
    SYSTEMTIME  st; 
 
    GetLocalTime(&st); 
 
cli->Active = TRUE; 
cli->tpconv = NULL; 
cli->SendOutput = TRUE; 
lstrcpy(cli->Name, TEXT("Local")); 
 
    if ((cli->rSaveFile=CreateFile(SaveFileName, 
            GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))==NULL) { 
cli->Active = FALSE; 
        return 0; 
    } 
 
    cli->MoreData = CreateEvent( 
        (LPSECURITY_ATTRIBUTES) NULL, /* address of security attributes*/ 
        FALSE,                      /* flag for manual-reset event*/ 
        TRUE,                      /* flag for initial state*/ 
        NULL                      /* address of event-object name*/ 
    ); 
 
cli->DoClose = CreateEvent( 
(LPSECURITY_ATTRIBUTES) NULL,// no security on event 
FALSE,// auto reset event mode 
FALSE,// initially false 
NULL);// unnamed object 
 
    WRITEF((VBuff, TEXT("\nServer: Connected To %ws [%02d:%02d]\n"),cli->Name,st.wHour,st.wMinute)); 
 
if ((wthread = CreateThread(NULL,// no security attributes 
0,// use same stack size 
(LPTHREAD_START_ROUTINE) StdinThread,// function to run 
cli,// pass client info 
0,// run now 
&tid)) == NULL) {// return thread id 
ExitThread(0); 
} 
 
if ((rthread = CreateThread(NULL,// no security attributes 
0,// use same stack size 
(LPTHREAD_START_ROUTINE) StdoutThread,// function to run 
cli,// pass client info 
0,// run now 
&tid)) == NULL) {// return thread id 
ExitThread(0); 
} 
 
WaitForSingleObject(cli->DoClose, INFINITE); 
 
TerminateThread(rthread, 0); 
TerminateThread(wthread, 0); 
 
cli->Active = FALSE; 
CloseHandle(cli->DoClose); 
CloseHandle(cli->MoreData); 
CloseHandle(cli->rSaveFile); 
CloseHandle(stdin); 
CloseHandle(stdout); 
 
    WRITEF((VBuff, TEXT("\nServer: Disconnected From %ws [%02d:%02d]\n"),cli->Name,st.wHour,st.wMinute)); 
 
return 0; 
} 
 
DWORD WINAPI StdinThread(SESSION_TYPE *cli) { 
HANDLEStdin; 
TCHARubuf[BUFFSIZE]; 
charabuf[BUFFSIZE]; 
DWORDtmp, dread, asize; 
 
Stdin = GetStdHandle(STD_INPUT_HANDLE); 
// read a command from the user into a buffer (it will be in Unicode) 
while (ReadConsole(Stdin, ubuf, BUFFSIZE - 1, &dread, NULL)) { 
cli->CommandRcvd = TRUE; 
ubuf[dread] = 0; 
// see if it is a valid command 
if (!FilterCommand(cli, ubuf, dread)) { 
// convert the buffer from Unicode to ANSI 
asize = wcstombs(abuf, ubuf, BUFFSIZE); 
 
// write the buffer to the child's pipe 
if (!WriteFile(ChildStdInp, abuf, asize, &tmp, NULL)) 
ExitThread(0); 
} 
} 
return 0; 
} 
 
DWORD WINAPI StdoutThread(SESSION_TYPE *cli) { 
HANDLEStdout; 
charabuffin[BUFFSIZE]; 
    TCHAR   buffin[BUFFSIZE], buffout[BUFFSIZE], cmdbuff[BUFFSIZE]; 
    DWORD  aread, dread=0, dwrite=0, tmp, cmdP = 0, i; 
    BOOL   incmd=FALSE; 
    TCHAR   MyEchoStr[30]; 
 
    wsprintf(MyEchoStr,TEXT("[%-15s"),cli->Name); 
 
    Stdout = GetStdHandle(STD_OUTPUT_HANDLE); 
 
// read a buffer from an ansi file (the buffer will be ANSI) 
    while (ReadFile(cli->rSaveFile, abuffin, BUFFSIZE-1, &aread, NULL)) { 
        if (aread == 0) { 
// wait for more data to arrive and loop again 
WaitForSingleObject(cli->MoreData, INFINITE); 
continue; 
} 
 
abuffin[aread] = 0; 
dread = mbstowcs(buffin, abuffin, BUFFSIZE); 
 
        dwrite=0; 
 
        // 
        // This is all to insure that the commands entered 
        // by clients are not echoed back to them. 
        // A Beginmark and an Endmark is placed around commands 
        // sent to the child process from some client. 
        // 
 
        for (i = 0; i < dread; i++) { 
            if (incmd) { 
                if ((buffin[i] == ENDMARK) || (cmdP == BUFFSIZE - 1)) { 
                    incmd = FALSE; 
                    cmdbuff[cmdP] = 0; 
                    if ((wcsstr(cmdbuff, MyEchoStr) == NULL) || 
                        (!cli->CommandRcvd)) { 
WriteConsole(Stdout, cmdbuff, dread, &tmp, NULL); 
                    } 
                    cmdP=0; 
                } else { 
                    cmdbuff[cmdP++] = buffin[i]; 
                } 
            } else { 
                if (buffin[i] == BEGINMARK) { 
                    if (dwrite != 0) { 
WriteConsole(Stdout, buffout, dread, &tmp, NULL); 
                        dwrite = 0; 
                    } 
                    incmd = TRUE; 
                    continue; 
                } else { 
                    buffout[dwrite++] = buffin[i]; 
                } 
            } 
        } 
 
        if (dwrite != 0) { 
WriteConsole(Stdout, buffout, dread, &tmp, NULL); 
        } 
    } 
 
return 0; 
} 
 
DWORD NewSession(SESSION_TYPE* MyClient) { 
    DWORD ThreadId; 
    HANDLE  rThread, wThread; 
 
    MyClient->MoreData = CreateEvent( 
        (LPSECURITY_ATTRIBUTES) NULL, /* address of security attributes*/ 
        FALSE,                      /* flag for manual-reset event*/ 
        TRUE,                      /* flag for initial state*/ 
        NULL                      /* address of event-object name*/ 
    ); 
 
MyClient->DoClose = CreateEvent( 
(LPSECURITY_ATTRIBUTES) NULL,// no security on event 
FALSE,// flag for manual-reset event 
FALSE,// initially false 
NULL);// unnamed object 
 
    if ((rThread = CreateThread( 
            (LPSECURITY_ATTRIBUTES) NULL,         // No security attributes. 
            (DWORD) 0,                            // Use same stack size. 
(LPTHREAD_START_ROUTINE) GetClientInput, 
            (LPVOID) MyClient,                    // Parameter to pass. 
            (DWORD) 0,                            // Run immediately. 
            (LPDWORD) &ThreadId))==NULL) { 
        return(GetLastError()); 
    } 
 
 
    if ((wThread=CreateThread( 
            (LPSECURITY_ATTRIBUTES) NULL,        // No security attributes. 
            (DWORD) 0,                           // Use same stack size. 
            (LPTHREAD_START_ROUTINE) TransferFileToClient, 
            (LPVOID) MyClient,                   // Parameter to pass. 
            (DWORD) 0,                           // Run immediately. 
            (LPDWORD) &ThreadId))==NULL) { 
        CloseHandle(rThread); 
        return(GetLastError()); 
    } 
 
// Wait for the input thread to terminate.  when it does the session is 
// over. 
    ThreadId = WaitForSingleObject(wThread, INFINITE); 
 
CloseClient(MyClient); 
 
ThreadId = WaitForSingleObject(rThread, INFINITE); 
 
// close the thread handles 
    CloseHandle(rThread); 
    CloseHandle(wThread); 
 
    return 0; 
} 
 
/*************************************************************/ 
/* Saves the output from the child process into the savefile */ 
/* All the remote client thread and local client thread      */ 
/* open a seperate handle to this and output its content     */ 
/* sequentially.                                             */ 
/*************************************************************/ 
DWORD WINAPI GetChldOutput(HANDLE readH) { 
    TCHAR  buff[BUFFSIZE]; 
    DWORD dread; 
    DWORD tmp; 
inti; 
 
 
    while (ReadFile(readH,buff,BUFFSIZE-1,&dread,NULL)) { 
        buff[dread]=TEXT('\0'); 
 
        if (!WriteFile(SaveFile,buff,dread,&tmp,NULL)) 
            return(1); 
 
        // 
        // Signal Reader Thread that more data is available 
        // 
        for (i=0;i<MAX_SESSION;i++) 
if (ClientList[i].Active) SetEvent(ClientList[i].MoreData); 
    } 
    return 1; 
} 
 
/*************************************************************/ 
/* A thread for each client connection and one for local IO  */ 
/* Reads the contents of Save file and sends it to client for*/ 
/* display.                                                  */ 
/*************************************************************/ 
DWORD WINAPI TransferFileToClient(SESSION_TYPE *MyClient) { 
    TCHAR   buffin[BUFFSIZE], buffout[BUFFSIZE], cmdbuff[BUFFSIZE]; 
charabuffin[BUFFSIZE]; 
    DWORD  dread=0,dwrite=0,aread; 
    BOOL   incmd=FALSE; 
    DWORD  cmdP=0; 
    DWORD  i; 
    TCHAR   MyEchoStr[30]; 
HANDLEobjs[2]; 
DWORD which; 
 
    wsprintf(MyEchoStr,TEXT("[%-15s"),MyClient->Name); 
 
    while (ReadFile(MyClient->rSaveFile, abuffin, BUFFSIZE-1, &aread, NULL)) { 
        if (aread == 0) { 
            // 
            // Event is set by GetChldOutput() func. to signal 
            // More data is available in save file. 
            // 
objs[0] = MyClient->MoreData; 
objs[1] = MyClient->DoClose; 
 
// wait for moredata or a close request 
which = WaitForMultipleObjects(2, objs, FALSE, INFINITE); 
which = which - WAIT_OBJECT_0; 
 
            if (which == 0) { 
// there is more data waiting 
continue; 
} else { 
// they want us to close 
return 0; 
} 
        } 
 
dread = mbstowcs(buffin, abuffin, aread); 
 
        dwrite=0; 
 
        // 
        // This is all to insure that the commands entered 
        // by clients are not echoed back to them. 
        // A Beginmark and an Endmark is placed around commands 
        // sent to the child process from some client. 
        // 
 
        for (i = 0; i < dread; i++) { 
            if (incmd) { 
                if ((buffin[i] == ENDMARK) || (cmdP == BUFFSIZE - 1)) { 
                    incmd = FALSE; 
                    cmdbuff[cmdP] = 0; 
                    if ((wcsstr(cmdbuff, MyEchoStr) == NULL) || 
                        (!MyClient->CommandRcvd)) { 
appcwrite(MyClient->tpconv, (void *) cmdbuff, 
cmdP * sizeof(TCHAR)); 
                    } 
                    cmdP=0; 
                } else { 
                    cmdbuff[cmdP++] = buffin[i]; 
                } 
            } else { 
                if (buffin[i] == BEGINMARK) { 
                    if (dwrite != 0) { 
appcwrite(MyClient->tpconv, (void *) buffout, 
dwrite * sizeof(TCHAR)); 
                        dwrite = 0; 
                    } 
                    incmd = TRUE; 
                    continue; 
                } else { 
                    buffout[dwrite++] = buffin[i]; 
                } 
            } 
        } 
 
        if (dwrite != 0) { 
appcwrite(MyClient->tpconv, (void *) buffout, 
dwrite * sizeof(TCHAR)); 
        } 
    } 
    return 0; 
} 
 
/*************************************************************/ 
/* Commands from the clients are sent to the child process   */ 
/* and also saved in the SaveFile with Begin and End markers */ 
/* around them to seperate them from the output from child   */ 
/* process.                                                  */ 
/*************************************************************/ 
DWORD WINAPI GetClientInput(SESSION_TYPE *MyClient) { 
    TCHAR ubuff[BUFFSIZE]; 
char  abuff[BUFFSIZE]; 
    DWORD tmp,uread,aread; 
 
while (TRUE) { 
    uread = appcread(MyClient->tpconv, (void *) ubuff, BUFFSIZE) / 
sizeof(TCHAR); 
if (!appcvalid(MyClient->tpconv)) break; 
 
        MyClient->CommandRcvd = TRUE; 
 
        if (FilterCommand(MyClient, ubuff, uread)) continue; 
 
aread = wcstombs(abuff, ubuff, uread); 
 
        if (!WriteFile(ChildStdInp, abuff, aread, &tmp, NULL)) ExitThread(0); 
    } 
 
    return(1); 
} 
 
/*************************************************************/ 
/* If a client command is intended for the Remote server -   */ 
/* those beginning with COMMANDCHAR (are not intended        */ 
/* for the child process) - they are executed here           */ 
/* and the output sent to the client.                        */ 
/*************************************************************/ 
BOOL FilterCommand(SESSION_TYPE *cl, TCHAR *buff, int dread) { 
    SYSTEMTIME st; 
    TCHAR       inp_buff[4096]; 
    TCHAR       tmpchar; 
    TCHAR       ch[3]; 
    DWORD      tmp; 
    int        len; 
    DWORD      ThreadID; //Useless 
 
    if (dread==0) 
        return(FALSE); 
 
    buff[dread]=0; 
 
    GetLocalTime(&st); 
 
    if (buff[0]==COMMANDCHAR) { 
        switch(tolower(buff[1])) { 
            case TEXT('o'): 
                cl->SendOutput=!cl->SendOutput; 
                break; 
 
            case TEXT('k'): 
                TerminateProcess(ChldProc,1); 
                break; 
 
case TEXT('q'): 
SetEvent(cl->DoClose); 
break; 
 
            case TEXT('s'): 
                SendStatus(cl->tpconv); 
                break; 
 
            case TEXT('p'): 
if (cl->tpconv == NULL) break; 
{ 
 //Free it in called Proc 
TCHAR *mssg=(TCHAR *)LocalAlloc(0, 4096*sizeof(TCHAR)); 
                TCHAR  *ack=TEXT("Server: Popup Shown..\n"); 
 
                    if (mssg==NULL) 
                        break; 
 
                    wsprintf(mssg,TEXT("From %ws [%d:%d]\n\n%ws\n"),cl->Name,st.wHour,st.wMinute,&buff[2]); 
                    CreateThread( 
                          (LPSECURITY_ATTRIBUTES)NULL,         // No security attributes. 
                          (DWORD)0,              // Use same stack size. 
                          (LPTHREAD_START_ROUTINE)ShowPopup, // Thread procedure. 
                          (LPVOID)mssg,          // Parameter to pass. 
                          (DWORD)0,              // Run immediately. 
                          (LPDWORD)&ThreadID 
                         ); 
appcwrite(cl->tpconv, (void *) ack, lstrlen(ack) * 
sizeof(TCHAR)); 
                    break; 
                 } 
 
            case TEXT('m'): 
                buff[dread-2]=0; 
                CMDSTRING(inp_buff,buff,cl,st); 
                len=lstrlen(inp_buff); 
                WriteFile(SaveFile,inp_buff,len,&tmp,NULL); 
                break; 
 
            case TEXT('@'): 
                buff[dread-2]=0; 
                CMDSTRING(inp_buff,&buff[1],cl,st); 
                len=lstrlen(inp_buff); 
                WriteFile(SaveFile,inp_buff,len,&tmp,NULL); 
                // 
                // Remove the first @ sign 
                // 
                MoveMemory(buff,&buff[1],dread-1); 
                buff[dread-1]=TEXT(' '); 
                return(FALSE); //Send it it to the chile process 
                break; 
 
 
            default : 
if (cl->tpconv == NULL) break; 
                lstrcpy(inp_buff, TEXT("** Unknown Command **\n")); 
appcwrite(cl->tpconv, (void *) inp_buff, 
lstrlen(inp_buff) * sizeof(TCHAR)); 
 
            case TEXT('h'): 
if (cl->tpconv != NULL) { 
                wsprintf(inp_buff,TEXT("%cM: To Send Message\n"),COMMANDCHAR); 
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR)); 
               wsprintf(inp_buff,TEXT("%cP: To Generate popup\n"),COMMANDCHAR); 
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR)); 
               wsprintf(inp_buff,TEXT("%cK: To kill the server\n"),COMMANDCHAR); 
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR)); 
               wsprintf(inp_buff,TEXT("%cH: This Help\n"),COMMANDCHAR); 
appcwrite(cl->tpconv, (void *) inp_buff, lstrlen(inp_buff) * sizeof(TCHAR)); 
} 
                break; 
        } 
        return(TRUE); 
    } 
 
 
    if ((buff[0]<26)) { 
        BOOL ret=FALSE; 
 
        wsprintf(ch,TEXT("^%c"),buff[0]+64); 
//        CMDSTRING(inp_buff,ch,cl,st); 
        len=lstrlen(inp_buff); 
 
        if (buff[0]==CTRLC) 
        { 
            cl->CommandRcvd=FALSE; 
            GenerateConsoleCtrlEvent(CTRL_C_EVENT,0); 
            ret=TRUE; //Already sent to child 
        } 
 
        WriteFile(SaveFile,inp_buff,len,&tmp,NULL); 
        return(ret); //FALSE:send it to child StdIn 
    } 
 
    tmpchar=buff[dread-2]; //must be 13;but just incase 
    buff[dread-2]=0; 
//    CMDSTRING(inp_buff,buff,cl,st); 
    buff[dread-2]=tmpchar; 
    len=lstrlen(inp_buff); 
    WriteFile(SaveFile,inp_buff,len,&tmp,NULL); 
    return(FALSE); 
} 
 
/*************************************************************/ 
VOID SendStatus(tpconvid_t tpconv) { 
    TCHAR  buff[1024]; 
    int   i; 
    TCHAR  *env=(TCHAR *)GetEnvironmentStrings(); 
    DWORD ver=GetVersion(); 
 
if (tpconv == NULL) return; 
 
    wsprintf(buff,TEXT("Command = %ws\n"), ChildCmd); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
 
    wsprintf(buff,TEXT("Build = %d \n"),((WORD *)&ver)[1]); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
 
    for (i=1;i<MAX_SESSION;i++) 
    { 
        if (ClientList[i].Active) 
        { 
            wsprintf(buff,TEXT("ACTIVE SESSION=%ws\n"),ClientList[i].Name); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
        } 
    } 
 
    wsprintf(buff,TEXT("====================\n")); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
 
    wsprintf(buff,TEXT("ENVIRONMENT VARIABLES\n")); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
 
    wsprintf(buff,TEXT("====================\n")); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
 
 
    __try { 
        while (*env!=0) 
        { 
            wsprintf(buff,TEXT("%ws\n"),env); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
 
            while(*(env++)!=0); 
        } 
    } __except(EXCEPTION_EXECUTE_HANDLER) { 
        wsprintf(buff,TEXT("Exception Generated Getting Environment Block\n"),env); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
    } 
 
    wsprintf(buff,TEXT("====================\n")); 
appcwrite(tpconv, (void *) buff, lstrlen(buff) * sizeof(TCHAR)); 
    return; 
} 
 
/*************************************************************/ 
DWORD WINAPI 
ShowPopup( 
    TCHAR *mssg 
    ) 
{ 
    MessageBox(GetActiveWindow(),mssg,TEXT("***REMOTE***"),MB_OK|MB_SETFOREGROUND); 
    LocalFree(mssg); 
    return(0); 
} 
/*************************************************************/ 
BOOL SrvCtrlHand( 
    DWORD event 
    ) 
{ 
    if (event==CTRL_BREAK_EVENT) 
    { 
        TerminateProcess(ChldProc,1); 
    } 
    return(TRUE); 
} 
/*************************************************************/ 
 
VOID CloseClient(SESSION_TYPE *Client) { 
EnterCriticalSection(&close_crit); 
 
appcclose(Client->tpconv); 
 
    if (Client->rSaveFile != INVALID_HANDLE_VALUE) { 
        CloseHandle(Client->rSaveFile); 
        Client->rSaveFile = INVALID_HANDLE_VALUE; 
    } 
 
    if (Client->MoreData != NULL) { 
        CloseHandle(Client->MoreData); 
        Client->MoreData = NULL; 
    } 
 
Client->Active=FALSE; //Keep it last else synch problem. 
 
LeaveCriticalSection(&close_crit); 
 
    return; 
} 
 
VOID InitClientList(VOID) { 
    int i; 
    for (i=0;i<MAX_SESSION;i++) 
    { 
        ZeroMemory(ClientList[i].Name,HOSTNAMELEN); 
ClientList[i].tpconv = NULL; 
        ClientList[i].rSaveFile = INVALID_HANDLE_VALUE; 
        ClientList[i].MoreData = NULL; 
        ClientList[i].Active = FALSE; 
        ClientList[i].CommandRcvd = FALSE; 
        ClientList[i].SendOutput = FALSE; 
        ClientList[i].hThread = NULL; 
    } 
    return; 
} 
 
 
 
VOID RemoveInpMark(char* Buff, DWORD Size) { 
    DWORD i; 
    for (i=0;i<Size;i++) 
    { 
        switch (Buff[i]) 
        { 
        case BEGINMARK: 
            Buff[i]=' '; 
            break; 
 
        case ENDMARK: 
            if (i<2) 
            { 
                Buff[i]= ' '; 
            } 
            else 
            { 
                Buff[i]  =Buff[i-1]; 
                Buff[i-1]=Buff[i-2]; 
                Buff[i-2]=' '; 
            } 
            break; 
 
        default: 
           break; 
       } 
    } 
}