#include "server.h"
#include "..\DynamicLink\serverblock.h"
#include "..\ClientChannel\channel.h"
#include "utilities.h"
#include "global.h"

static char *b = NULL;
static DWORD ClientProcessId = 0;
static HANDLE ClientProcessHandle = NULL;

#define INIT_SERVER				0
#define INIT_CHANNEL			1
#define PROCESS_REQUESTS		2
#define SET_GLOBAL_SERVER_READY	3

static state = INIT_SERVER;

//
static HANDLE hClient = NULL;

/*
**
*/
#define TEXT	0
#define DATA	1

static int *CodeBuffer = NULL;
static int *DataBuffer = NULL;
static int CodeStart   = 0;
static int DataStart   = 0;
static int CodeSize		= 0;
static int DataSize		= 0;

CLEAN_BOOL StoreLong(int pvMem, int l)
{
	char *dest;

	dest = (((int) CodeBuffer) + (pvMem - CodeStart));

	*((int *) dest) = l;

    return( CLEAN_TRUE );
}

int mwrites(int kind, int offset, CLEAN_STRING s, int address)
{
	char *dest;
	int i;


//		__asm int 3;

	if (kind == TEXT) {
		dest = ((int) CodeBuffer) + offset;
	}
	else {
		dest = ((int) DataBuffer) + offset;
	}

	for( i = 0; i < (s->length); i++)
	{
		dest[i] = (s->characters)[i];
	}


	return (address + (s->length));
}

// ServerInfoBlock
static PSERVER_INFO_BLOCK pSIB = NULL;

#define CONNECTION_INFO_IN_ENV
#ifdef CONNECTION_INFO_IN_ENV
void StartProcess (CLEAN_BOOL is_console_client, CLEAN_STRING cg, CLEAN_STRING Title, CLEAN_STRING CmdLine, CLEAN_STRING Environment,
				   CLEAN_BOOL *ok, int *client_id, CLEAN_STRING *result_path)
{
	// ServerInfoBlock
	DWORD dwServerId;
	HANDLE hServerReady;
	HANDLE hClientReady;
	HANDLE hFileMapping;
	DWORD offset;

	PROCESS_INFORMATION pi;

	STARTUPINFO si;
	
	BOOL fSuccess;					// Process information
	BOOL try_again;

	BOOL is_cg_path_statically_allocated;

	// 
	char *dir;
	char *cmdline;
	char *cg_path;
//	char *env;

	char env[1000];
	char *envP;
	char *s;
	int i;

	static CLEAN_STRING console_or_gui_path = NULL;

	if( console_or_gui_path != NULL ) {
		rfree( console_or_gui_path );
		console_or_gui_path = NULL;
	}

//	__asm int 3

	cg_path = (char *) rmalloc ((cg->length) + 1);
	rsncopy (cg_path,cg->characters, cg->length);
	cg_path[cg->length] = 0;
	is_cg_path_statically_allocated = FALSE;

//	env = (char *) rmalloc ((Environment->length) + 1);
//	rsncopy (env, Environment->characters, Environment->length );
//	env[Environment->length] = 0;

	// ServerInfoBlock
	AddClient_to_ServerInfoBlock( 
		pSIB, 
		&dwServerId, 
		&hServerReady, 
		&hClientReady,
		&hFileMapping,
		&offset);

	// Commandline:
	// dummy <server id> <hServerReady> <hClientReady> <hFileMapping> <offset> <length of buffer>
	cmdline = (char *) rmalloc ( 200 );

	// sprintf
//	__asm int 3


	// ith position contains '\0'
	envP = env;

	i = sprintf( envP, "dwServerId=%d", dwServerId); envP = envP + i + 1;
	i = sprintf( envP, "hServerReady=%d", hServerReady); envP = envP + i + 1;
	i = sprintf( envP, "hClientReady=%d", hClientReady); envP = envP + i + 1;
	i = sprintf( envP, "hFileMapping=%d", hFileMapping); envP = envP + i + 1;
	i = sprintf( envP, "offset=%d", offset); envP = envP + i + 1;
	i = sprintf( envP, "MESSAGE_SIZE=%d", MESSAGE_SIZE); envP = envP + i + 1;

	*envP = '\0';

	/*
	__asm int 3
	// 
	s = cmdline;
	while ( *s != ' ' )
		s++;

	cmdline[0] = '\0';
	*/


/*1
	rsprintf( cmdline, "%d %d %d %d %d %d", 
		dwServerId, 
		hServerReady, 
		hClientReady,
		hFileMapping,
		offset,
		MESSAGE_SIZE);
*/

//	msg( cmdline );

	
	// Convert workdir
	dir = (char *) rmalloc ((Title->length) + 1);
	rsncopy (dir,Title->characters, Title->length);
	dir[Title->length] = 0;

	// StartUpInfo
	for (i=0; i < sizeof(si); i++)
		((char *) &si)[i] = (char) 0;

	//ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);

	si.lpTitle = dir;
	si.wShowWindow = SW_SHOWNORMAL;
	si.dwFlags = STARTF_USESHOWWINDOW;

	// Create new process
	do {
		try_again = FALSE;

		fSuccess = CreateProcess( cg_path,	// Executable
			cmdline,				// Commandline
			NULL,					// Standard security (Proces)
			NULL,					// Standard security (Thread)
			TRUE,					// No handle inherited by child
			CREATE_NEW_CONSOLE,		// Plain process
			env,					// Environment of parent
			NULL, /*dir,*/			// Current directory
			&si,					// Startup information
			&pi);					// Process information

		if( fSuccess == NULL ) {
			switch( GetLastError() ) {
				case ERROR_FILE_NOT_FOUND:
					rfree(cg_path);

					msg( "server.c: GUI OR CONSOLE CLIENT NOT FOUND" );

					/*
					cg_path = (is_console_client == CLEAN_TRUE) ?
						SetConsoleOrGuiClient(CONSOLECLIENT) :
						SetConsoleOrGuiClient(GUICLIENT);
					*/
					 is_cg_path_statically_allocated = TRUE;

					if( cg_path == NULL ) {
						/*
						** Free strings
						*/
						rfree(dir);
						rfree(cmdline);

						/*
						** The user canceled the dialogue, then the
						** process cannot be started
						*/
						*ok = CLEAN_FALSE;
						*client_id = 0;
						*result_path = EmptyCleanString;
						return;
					}

					/*
					** cgpath contains a path, try again
					*/
					try_again = TRUE;
					break;

				default:
					msg( "StartProcess: cannot handle error" );
					ExitProcess(-1);
					break;
			}
		}

	} while (try_again);

	/*
	** The process has been started, free resources
	*/
	rfree( dir );
	rfree( cmdline );

	CloseHandle(pi.hThread);

	UpdateClient_in_ServerInfoBlock( 
		pSIB, 
		pi.hProcess, 
		pi.dwProcessId);

	hClient = pi.hProcess;

	*ok = CLEAN_TRUE;
	*client_id = pi.dwProcessId;

	if( !is_cg_path_statically_allocated ) {
		/*
		** cg_path is dynamically allocated which implies that the user has 
		** not selected a (new) console or gui client.
		*/
		rfree( cg_path );
		*result_path = EmptyCleanString;
	}
	else {
		/*
		** The console or gui client executable could not be found. The user
		** has supplied a new path. Pass this on to clean
		*/
		console_or_gui_path = (CLEAN_STRING) rmalloc (sizeof (int) + rstrlen(cg_path) + 1);
		console_or_gui_path->length = rstrlen( cg_path );
		rsncopy( console_or_gui_path->characters, cg_path, rstrlen(cg_path) + 1);

		*result_path = console_or_gui_path;
	}
}

// ---------------------------------------------------------------------------------
void StartProcess2(CLEAN_STRING current_directory, CLEAN_STRING file_name, CLEAN_STRING commandline,
				   CLEAN_BOOL *ok, int *client_id)
{
	// ServerInfoBlock
	DWORD dwServerId;
	HANDLE hServerReady;
	HANDLE hClientReady;
	HANDLE hFileMapping;
	DWORD offset;

	PROCESS_INFORMATION pi;

	STARTUPINFO si;
	
	BOOL fSuccess;					// Process information
	BOOL try_again;

	BOOL is_cg_path_statically_allocated;

	// 
	char *dir;
	char *cmdline;
	char *cg_path;
//	char *env;

	char env[1000];
	char *envP;
	char *s;
	int i;

//	__asm int 3
	// ServerInfoBlock
	AddClient_to_ServerInfoBlock( 
		pSIB, 
		&dwServerId, 
		&hServerReady, 
		&hClientReady,
		&hFileMapping,
		&offset);

//	__asm int 3


	// create environment
	envP = env;

//	i = sprintf( envP, "a=b"); envP = envP + i + 1;
//	i = sprintf( envP, "dwServerId=-%d", 1); envP = envP + i + 1;
//	i = sprintf( envP, "dwServerId=%u", dwServerId); envP = envP + i + 1;

	
	i = sprintf( envP, "dwServerId=%d", dwServerId); envP = envP + i + 1;	
	i = sprintf( envP, "hServerReady=%d", hServerReady); envP = envP + i + 1;
	i = sprintf( envP, "hClientReady=%d", hClientReady); envP = envP + i + 1;
	i = sprintf( envP, "hFileMapping=%d", hFileMapping); envP = envP + i + 1;
	i = sprintf( envP, "offset=%d", offset); envP = envP + i + 1;
	i = sprintf( envP, "MESSAGE_SIZE=%d", MESSAGE_SIZE); envP = envP + i + 1;
//	i = sprintf( envP, "a=%u", dwServerId); envP = envP + i + 1;	

	*envP = '\0';

//	sprintf( env, "a=b\0\0");

	// StartUpInfo
//	for (i=0; i < sizeof(si); i++)
//		((char *) &si)[i] = (char) 0;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);

	si.lpTitle = file_name->characters;
	si.wShowWindow = SW_SHOWNORMAL;
	si.dwFlags = STARTF_USESHOWWINDOW;

	fSuccess = CreateProcess( 
		NULL,							// Executable
		commandline->characters,		// Commandline
		NULL,							// Standard security (Proces)
		NULL,							// Standard security (Thread)
		TRUE,							// No handle inherited by child
		CREATE_NEW_CONSOLE,				// Plain process
		env,							// Environment of parent
		current_directory->characters, /*dir,*/				// Current directory
		&si,							// Startup information
		&pi);							// Process information

	if( !fSuccess ) {
		*ok = 0;
		*client_id = 0;
	} else {
		CloseHandle(pi.hThread);

		UpdateClient_in_ServerInfoBlock( 
			pSIB, 
			pi.hProcess, 
			pi.dwProcessId);

			hClient = pi.hProcess;

		*ok = CLEAN_TRUE;
		*client_id = pi.dwProcessId;
	}


	return;

#ifdef OMIT
	// Create new process
	do {
		try_again = FALSE;



		if( fSuccess == NULL ) {
			switch( GetLastError() ) {
				case ERROR_FILE_NOT_FOUND:
					rfree(cg_path);

					msg( "server.c: GUI OR CONSOLE CLIENT NOT FOUND" );

					/*
					cg_path = (is_console_client == CLEAN_TRUE) ?
						SetConsoleOrGuiClient(CONSOLECLIENT) :
						SetConsoleOrGuiClient(GUICLIENT);
					*/
					 is_cg_path_statically_allocated = TRUE;

					if( cg_path == NULL ) {
						/*
						** Free strings
						*/
						rfree(dir);
						rfree(cmdline);

						/*
						** The user canceled the dialogue, then the
						** process cannot be started
						*/
						*ok = CLEAN_FALSE;
						*client_id = 0;
						*result_path = EmptyCleanString;
						return;
					}

					/*
					** cgpath contains a path, try again
					*/
					try_again = TRUE;
					break;

				default:
					msg( "StartProcess: cannot handle error" );
					ExitProcess(-1);
					break;
			}
		}

	} while (try_again);

	/*
	** The process has been started, free resources
	*/
	rfree( dir );
	rfree( cmdline );

	CloseHandle(pi.hThread);

	UpdateClient_in_ServerInfoBlock( 
		pSIB, 
		pi.hProcess, 
		pi.dwProcessId);

	hClient = pi.hProcess;

	*ok = CLEAN_TRUE;
	*client_id = pi.dwProcessId;

	if( !is_cg_path_statically_allocated ) {
		/*
		** cg_path is dynamically allocated which implies that the user has 
		** not selected a (new) console or gui client.
		*/
		rfree( cg_path );
		*result_path = EmptyCleanString;
	}
	else {
		/*
		** The console or gui client executable could not be found. The user
		** has supplied a new path. Pass this on to clean
		*/
		console_or_gui_path = (CLEAN_STRING) rmalloc (sizeof (int) + rstrlen(cg_path) + 1);
		console_or_gui_path->length = rstrlen( cg_path );
		rsncopy( console_or_gui_path->characters, cg_path, rstrlen(cg_path) + 1);

		*result_path = console_or_gui_path;
	}
#endif 
}

#else
void StartProcess (CLEAN_BOOL is_console_client, CLEAN_STRING cg, CLEAN_STRING Title, CLEAN_STRING CmdLine, 
				   CLEAN_BOOL *ok, int *client_id, CLEAN_STRING *result_path)
{
	// ServerInfoBlock
	DWORD dwServerId;
	HANDLE hServerReady;
	HANDLE hClientReady;
	HANDLE hFileMapping;
	DWORD offset;

	PROCESS_INFORMATION pi;

	STARTUPINFO si;
	
	BOOL fSuccess;					// Process information
	BOOL try_again;

	BOOL is_cg_path_statically_allocated;

	// 
	char *dir;
	char *cmdline;
	char *cg_path;
	int i;

	static CLEAN_STRING console_or_gui_path = NULL;

	if( console_or_gui_path != NULL ) {
		rfree( console_or_gui_path );
		console_or_gui_path = NULL;
	}

	__asm int 3

	cg_path = (char *) rmalloc ((cg->length) + 1);
	rsncopy (cg_path,cg->characters, cg->length);
	cg_path[cg->length] = 0;
	is_cg_path_statically_allocated = FALSE;

	// ServerInfoBlock
	AddClient_to_ServerInfoBlock( 
		pSIB, 
		&dwServerId, 
		&hServerReady, 
		&hClientReady,
		&hFileMapping,
		&offset);

	// Commandline:
	// dummy <server id> <hServerReady> <hClientReady> <hFileMapping> <offset> <length of buffer>
	cmdline = (char *) rmalloc ( 200 );

	rsprintf( cmdline, "%d %d %d %d %d %d", 
		dwServerId, 
		hServerReady, 
		hClientReady,
		hFileMapping,
		offset,
		MESSAGE_SIZE);

//	msg( cmdline );

	
	// Convert workdir
	dir = (char *) rmalloc ((Title->length) + 1);
	rsncopy (dir,Title->characters, Title->length);
	dir[Title->length] = 0;

	// StartUpInfo
	for (i=0; i < sizeof(si); i++)
		((char *) &si)[i] = (char) 0;

	//ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);

	si.lpTitle = dir;
	si.wShowWindow = SW_SHOWNORMAL;
	si.dwFlags = STARTF_USESHOWWINDOW;

	// Create new process
	do {
		try_again = FALSE;

		fSuccess = CreateProcess( cg_path,	// Executable
			cmdline,				// Commandline
			NULL,					// Standard security (Proces)
			NULL,					// Standard security (Thread)
			TRUE,					// No handle inherited by child
			CREATE_NEW_CONSOLE,		// Plain process
			NULL,					// Environment of parent
			NULL, /*dir,*/			// Current directory
			&si,					// Startup information
			&pi);					// Process information

		if( fSuccess == NULL ) {
			switch( GetLastError() ) {
				case ERROR_FILE_NOT_FOUND:
					rfree(cg_path);

					msg( "server.c: GUI OR CONSOLE CLIENT NOT FOUND" );

					/*
					cg_path = (is_console_client == CLEAN_TRUE) ?
						SetConsoleOrGuiClient(CONSOLECLIENT) :
						SetConsoleOrGuiClient(GUICLIENT);
					*/
					 is_cg_path_statically_allocated = TRUE;

					if( cg_path == NULL ) {
						/*
						** Free strings
						*/
						rfree(dir);
						rfree(cmdline);

						/*
						** The user canceled the dialogue, then the
						** process cannot be started
						*/
						*ok = CLEAN_FALSE;
						*client_id = 0;
						*result_path = EmptyCleanString;
						return;
					}

					/*
					** cgpath contains a path, try again
					*/
					try_again = TRUE;
					break;

				default:
					msg( "StartProcess: cannot handle error" );
					ExitProcess(-1);
					break;
			}
		}

	} while (try_again);

	/*
	** The process has been started, free resources
	*/
	rfree( dir );
	rfree( cmdline );

	CloseHandle(pi.hThread);

	UpdateClient_in_ServerInfoBlock( 
		pSIB, 
		pi.hProcess, 
		pi.dwProcessId);

	hClient = pi.hProcess;

	*ok = CLEAN_TRUE;
	*client_id = pi.dwProcessId;

	if( !is_cg_path_statically_allocated ) {
		/*
		** cg_path is dynamically allocated which implies that the user has 
		** not selected a (new) console or gui client.
		*/
		rfree( cg_path );
		*result_path = EmptyCleanString;
	}
	else {
		/*
		** The console or gui client executable could not be found. The user
		** has supplied a new path. Pass this on to clean
		*/
		console_or_gui_path = (CLEAN_STRING) rmalloc (sizeof (int) + rstrlen(cg_path) + 1);
		console_or_gui_path->length = rstrlen( cg_path );
		rsncopy( console_or_gui_path->characters, cg_path, rstrlen(cg_path) + 1);

		*result_path = console_or_gui_path;
	}
}
#endif

void ReceiveReqWithTimeOut(CLEAN_BOOL static_application_as_client, CLEAN_BOOL *timeout,int *client_id, CLEAN_STRING *result)
{
	char *s;
	static CLEAN_STRING cs = NULL;

	int ClientId;

	// ServerInfoBlock
	DWORD dwResult;
	int Client_i;
	DWORD dwMilliSeconds;

	// ServerInfoBlock
	DWORD dwServerId;
	HANDLE hServerReady;
	HANDLE hClientReady;
	HANDLE hFileMapping;
	DWORD offset;

	HANDLE hClient;
	HANDLE hServer;

	BOOL ok;
	int length;

	*timeout = CLEAN_FALSE;
//	msg( "WAIT_FAILED" );
	
#ifdef RECEIVEREQ
	msg( "ReceiveReq 1" );

#endif

	if( cs != NULL ) {
		rfree( cs );
		cs = NULL;
	}

#ifdef RECEIVEREQ
	msg( "ReceiveReq 2" );
#endif

	if( state == INIT_SERVER ) {
		state = INIT_CHANNEL;

		// ServerInfoBlock
		pSIB = Empty_ServerInfoBlock();
	    AddInitialSyncs_to_ServerInfoBlock( pSIB );

		if( static_application_as_client == CLEAN_FALSE ) {
			cs = (CLEAN_STRING) rmalloc (sizeof(int)+12+rstrlen(GetCommandLine()) + 1);

			//                         0123456789 * 0 
			rsprintf( cs->characters, "AddClient\n%s\n\n", GetCommandLine() );
			cs->length = rstrlen(cs->characters);

			*client_id = 0;
			*result = cs;
			return;
		}

	
		/*
		** Static application as client
		*/
	}
	else if( state == INIT_CHANNEL ) {
		state = PROCESS_REQUESTS;
	}
	else if( state == SET_GLOBAL_SERVER_READY ) {
		/*
		** Settings this flag later guarantees round-robin fashion
		** of serving clients. Not doing this, guarantees that 
		** registering clients take priority.
		*/
		SetEvent( pSIB->hClientKilledOrReady[GLOBAL_SERVER_READY] );
		state = PROCESS_REQUESTS;
	}

//	sprintf( q, "No. of clients: %d", pSIB->n_Clients );
//	msg( q );


	/*
old:

	dwMilliSeconds = INFINITE;
	if( pSIB->n_Clients == (N_OF_RESERVED_ENTRIES_INITIAL_BLOCK / 2) &&
		!(static_application_as_client == CLEAN_TRUE)	//!!
	) {
		cs = (CLEAN_STRING) rmalloc(sizeof(int) + 7 );
		cs->length = 5;
		//						  01234
		rsncopy( cs->characters, "Quit\n", 5);
		*result = cs;
		*client_id = 0;
		return;
	}
	*/

//	while(1)
//		;

	// ServerInfoBlock, only initial block
	dwResult = WaitForMultipleObjects(
		(pSIB->n_Clients * 2) - 1,
		&(pSIB->hClientKilledOrReady[LOCAL_CLIENT_READY]),
		FALSE,
		10);//INFINITE );

	if( dwResult == /*WAIT_FAILED*/ WAIT_TIMEOUT ) {
		*timeout = CLEAN_TRUE;

	//	msg( "WAIT_TIMEOUT" );

		cs = (CLEAN_STRING) rmalloc (sizeof(int)+ 12);
		cs->length = 0;

		*result = cs;
		*client_id = 0;
		return;

		/*
		if( static_application_as_client == CLEAN_FALSE ) {
			cs = (CLEAN_STRING) rmalloc (sizeof(int)+12+rstrlen(GetCommandLine()) + 1);

			//                         0123456789 * 0 
			rsprintf( cs->characters, "AddClient\n%s\n\n", GetCommandLine() );
			cs->length = rstrlen(cs->characters);

			*client_id = 0;
			*result = cs;
			return;
  */
	
		error();
		msg( "WAIT_FAILED: jaa!!server.c" );
		ExitProcess(-1);
	}

	Client_i = dwResult - WAIT_OBJECT_0 + 1;
#ifdef DEBUG
	rsprintf( q, "Client #%d with %d", Client_i / 2, Client_i  % 2);
	msg( q );
#endif DEBUG

	/*
	** Protocol message has been received, the following code handles
	** the messages
	*/

	if( Client_i < 2 ) {

		if( Client_i != LOCAL_CLIENT_READY ) {
			msg( "local client should be ready" );
			ExitProcess(-1);
		}

		b = pSIB->hView[INDEX(Client_i)];

		ClientId = *((DWORD *) (b+GLOBAL_BUFFER_START));

		
//		msg( b + sizeof(DWORD) + GLOBAL_BUFFER_START );


		cs = (CLEAN_STRING) rmalloc(sizeof(int) + rstrlen( b + sizeof(DWORD) + GLOBAL_BUFFER_START) + 1);
		cs->length = rstrlen( b + sizeof(DWORD) + GLOBAL_BUFFER_START );
		rscopy(cs->characters, b + sizeof(DWORD) + GLOBAL_BUFFER_START);

//		msg( b + sizeof(DWORD) + GLOBAL_BUFFER_START );

#ifdef DEBUG
		msg( cs->characters );
#endif 	
		state = SET_GLOBAL_SERVER_READY;

		if( ClientId != UNKNOWN_CLIENT_ID ) {
			/*
			** An AddAndInit--protocol. Reply by sending the handles
			** to the required synchronization objects.
			*/
			// ServerInfoBlock
			AddClient_to_ServerInfoBlock( 
				pSIB, 
				&dwServerId, 
				&hServerReady, 
				&hClientReady,
				&hFileMapping,
				&offset);

			hClient = OpenProcess(STANDARD_RIGHTS_REQUIRED | PROCESS_ALL_ACCESS, FALSE, ClientId);
			if( hClient == NULL ) {
				error();
				msg( "ReceiveReq: could not open handle to statically linked client" );
				ExitProcess(-1);
			}

			UpdateClient_in_ServerInfoBlock( 
				pSIB, 
				hClient, 
				ClientId);

			/*
			** Open handle to itself
			*/
			hServer = OpenProcess(STANDARD_RIGHTS_REQUIRED | PROCESS_ALL_ACCESS, FALSE, dwServerId);
			if( hServer == NULL ) {
				error();
				msg( "ReceiveReq: could not open handle to itself, the dynamic linker" );
				ExitProcess(-1);
			}
			
			/*
			** Fill buffer
			*/
			*((DWORD *) (b+GLOBAL_BUFFER_START)) = dwServerId;
			ok = DuplicateHandle(hServer, hServerReady, hClient,
				((HANDLE *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) )), NULL, FALSE, DUPLICATE_SAME_ACCESS);
			if( !ok ) {
				error();
				msg( "Receivereq: could not dup handle" );
				ExitProcess(-1);
			}

			ok = DuplicateHandle(hServer, hClientReady, hClient,
				((HANDLE *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) + sizeof(HANDLE) )), NULL, FALSE, DUPLICATE_SAME_ACCESS);
			if( !ok ) {
				error();
				msg( "Receivereq: could not dup handle" );
				ExitProcess(-1);
			}

			ok = DuplicateHandle(hServer, hFileMapping, hClient,
				((HANDLE *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) + 2 * sizeof(HANDLE) )), NULL, FALSE, DUPLICATE_SAME_ACCESS);
			if( !ok ) {
				error();
				msg( "Receivereq: could not dup handle" );
				ExitProcess(-1);
			}

			*((DWORD *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) + 3 * sizeof(HANDLE) )) = offset;
			*((DWORD *) (b+ GLOBAL_BUFFER_START + 2 * sizeof(DWORD) + 3 * sizeof(HANDLE) )) = MESSAGE_SIZE;
			
			// Send to client
			SetEvent( pSIB->hServerReady[LOCAL_SERVER_READY] );

			// Wait for confirmation that Client processed buffer
			WaitForSingleObject( pSIB->hClientKilledOrReady[LOCAL_CLIENT_READY], INFINITE );

			// Signal Client
			SetEvent( pSIB->hServerReady[LOCAL_SERVER_READY] );

//			msg( "Server gaat verder" );

		}
		else 
			SetEvent( pSIB->hServerReady[LOCAL_SERVER_READY] );

		*client_id = ClientId;
		*result = cs;
		return;
	}

	if( IS_CLIENT_KILLED(Client_i) ) {

		*client_id = pSIB->ProcessId[ INDEX( Client_i ) ];

		RemoveClient_from_ServerInfoBlock( pSIB, (INDEX( Client_i )) );


		cs = (CLEAN_STRING) rmalloc(sizeof(int) + 7 );
		cs->length = 7;
		//						  012345 6
		rsncopy( cs->characters, "Close\n\n", 7);
		*result = cs;

		//msg( "killed" );

		return; 

		//ExitProcess(-1);
	}

	if( IS_CLIENT_READY(Client_i) )
		;

#ifdef DEBUG
	msg( "client ready" );
#endif

	// Prepare communication channel
	b = (pSIB->hView[INDEX(Client_i)]) + (int) (pSIB->Offset[INDEX(Client_i)]);
	SetHandlesToClient(
		pSIB->hServerReady[INDEX(Client_i)],
		pSIB->hClientKilledOrReady[INDEX_CLIENT_KILLED(Client_i)],
		pSIB->hClientKilledOrReady[INDEX_CLIENT_READY(Client_i)] );


	length = *((DWORD *) (b+SIZE_OF_MESSAGE));

	ClientProcessId = *((DWORD *) (b+DATA_START));
	ClientProcessHandle = pSIB->hClientKilledOrReady[INDEX_CLIENT_KILLED(Client_i)];

	s = b+DATA_START+sizeof(DWORD);	

	cs = (CLEAN_STRING) rmalloc (sizeof(int) + /*rstrlen(s) + 1*/ length );
	rsncopy( cs->characters, s, length);
	cs->length = length;  rstrlen(s);

	(*result) = cs;
	*client_id = pSIB->ProcessId[ INDEX(Client_i) ];

	return;

}

// original:
void ReceiveReq(CLEAN_BOOL static_application_as_client, int *client_id, CLEAN_STRING *result)
{
	char *s;
	static CLEAN_STRING cs = NULL;

	int ClientId;

	// ServerInfoBlock
	DWORD dwResult;
	int Client_i;
	DWORD dwMilliSeconds;

	// ServerInfoBlock
	DWORD dwServerId;
	HANDLE hServerReady;
	HANDLE hClientReady;
	HANDLE hFileMapping;
	DWORD offset;

	HANDLE hClient;
	HANDLE hServer;

	BOOL ok;
	int length;
	
#ifdef RECEIVEREQ
	msg( "ReceiveReq 1" );
#endif

	if( cs != NULL ) {
		rfree( cs );
		cs = NULL;
	}

#ifdef RECEIVEREQ
	msg( "ReceiveReq 2" );
#endif

	if( state == INIT_SERVER ) {
		state = INIT_CHANNEL;
		
		// ServerInfoBlock
		pSIB = Empty_ServerInfoBlock();
	    AddInitialSyncs_to_ServerInfoBlock( pSIB );

		if( static_application_as_client == CLEAN_FALSE ) {
			cs = (CLEAN_STRING) rmalloc (sizeof(int)+12+rstrlen(GetCommandLine()) + 1);

			//                         0123456789 * 0 
			rsprintf( cs->characters, "AddClient\n%s\n\n", GetCommandLine() );
			cs->length = rstrlen(cs->characters);

			*client_id = 0;
			*result = cs;
			return;
		}

	
		/*
		** Static application as client
		*/
	}
	else if( state == INIT_CHANNEL ) {
		state = PROCESS_REQUESTS;
	}
	else if( state == SET_GLOBAL_SERVER_READY ) {
		/*
		** Settings this flag later guarantees round-robin fashion
		** of serving clients. Not doing this, guarantees that 
		** registering clients take priority.
		*/
		SetEvent( pSIB->hClientKilledOrReady[GLOBAL_SERVER_READY] );
		state = PROCESS_REQUESTS;
	}

//	sprintf( q, "No. of clients: %d", pSIB->n_Clients );
//	msg( q );

	dwMilliSeconds = INFINITE;
	if( pSIB->n_Clients == (N_OF_RESERVED_ENTRIES_INITIAL_BLOCK / 2) &&
		!(static_application_as_client == CLEAN_TRUE)	//!!
	) {
		cs = (CLEAN_STRING) rmalloc(sizeof(int) + 7 );
		cs->length = 5;
		//						  01234
		rsncopy( cs->characters, "Quit\n", 5);
		*result = cs;
		*client_id = 0;
		return;
	}

	// ServerInfoBlock, only initial block
	dwResult = WaitForMultipleObjects(
		(pSIB->n_Clients * 2) - 1,
		&(pSIB->hClientKilledOrReady[LOCAL_CLIENT_READY]),
		FALSE,
		/*dwMilliSeconds*/ INFINITE );

	if( dwResult == WAIT_FAILED ) {
	
		error();
		msg( "WAIT_FAILED: server.c" );
		ExitProcess(-1);
	}

	Client_i = dwResult - WAIT_OBJECT_0 + 1;
#ifdef DEBUG
	rsprintf( q, "Client #%d with %d", Client_i / 2, Client_i  % 2);
	msg( q );
#endif DEBUG

	/*
	** Protocol message has been received, the following code handles
	** the messages
	*/

	if( Client_i < 2 ) {

		if( Client_i != LOCAL_CLIENT_READY ) {
			msg( "local client should be ready" );
			ExitProcess(-1);
		}

		b = pSIB->hView[INDEX(Client_i)];

		ClientId = *((DWORD *) (b+GLOBAL_BUFFER_START));

		
//		msg( b + sizeof(DWORD) + GLOBAL_BUFFER_START );


		cs = (CLEAN_STRING) rmalloc(sizeof(int) + rstrlen( b + sizeof(DWORD) + GLOBAL_BUFFER_START) + 1);
		cs->length = rstrlen( b + sizeof(DWORD) + GLOBAL_BUFFER_START );
		rscopy(cs->characters, b + sizeof(DWORD) + GLOBAL_BUFFER_START);

//		msg( b + sizeof(DWORD) + GLOBAL_BUFFER_START );

#ifdef DEBUG
		msg( cs->characters );
#endif 	
		state = SET_GLOBAL_SERVER_READY;

		if( ClientId != UNKNOWN_CLIENT_ID ) {
			/*
			** An AddAndInit--protocol. Reply by sending the handles
			** to the required synchronization objects.
			*/
			// ServerInfoBlock
			AddClient_to_ServerInfoBlock( 
				pSIB, 
				&dwServerId, 
				&hServerReady, 
				&hClientReady,
				&hFileMapping,
				&offset);

			hClient = OpenProcess(STANDARD_RIGHTS_REQUIRED | PROCESS_ALL_ACCESS, FALSE, ClientId);
			if( hClient == NULL ) {
				error();
				msg( "ReceiveReq: could not open handle to statically linked client" );
				ExitProcess(-1);
			}

			UpdateClient_in_ServerInfoBlock( 
				pSIB, 
				hClient, 
				ClientId);

			/*
			** Open handle to itself
			*/
			hServer = OpenProcess(STANDARD_RIGHTS_REQUIRED | PROCESS_ALL_ACCESS, FALSE, dwServerId);
			if( hServer == NULL ) {
				error();
				msg( "ReceiveReq: could not open handle to itself, the dynamic linker" );
				ExitProcess(-1);
			}
			
			/*
			** Fill buffer
			*/
			*((DWORD *) (b+GLOBAL_BUFFER_START)) = dwServerId;
			ok = DuplicateHandle(hServer, hServerReady, hClient,
				((HANDLE *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) )), NULL, FALSE, DUPLICATE_SAME_ACCESS);
			if( !ok ) {
				error();
				msg( "Receivereq: could not dup handle" );
				ExitProcess(-1);
			}

			ok = DuplicateHandle(hServer, hClientReady, hClient,
				((HANDLE *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) + sizeof(HANDLE) )), NULL, FALSE, DUPLICATE_SAME_ACCESS);
			if( !ok ) {
				error();
				msg( "Receivereq: could not dup handle" );
				ExitProcess(-1);
			}

			ok = DuplicateHandle(hServer, hFileMapping, hClient,
				((HANDLE *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) + 2 * sizeof(HANDLE) )), NULL, FALSE, DUPLICATE_SAME_ACCESS);
			if( !ok ) {
				error();
				msg( "Receivereq: could not dup handle" );
				ExitProcess(-1);
			}

			*((DWORD *) (b+ GLOBAL_BUFFER_START + sizeof(DWORD) + 3 * sizeof(HANDLE) )) = offset;
			*((DWORD *) (b+ GLOBAL_BUFFER_START + 2 * sizeof(DWORD) + 3 * sizeof(HANDLE) )) = MESSAGE_SIZE;
			
			// Send to client
			SetEvent( pSIB->hServerReady[LOCAL_SERVER_READY] );

			// Wait for confirmation that Client processed buffer
			WaitForSingleObject( pSIB->hClientKilledOrReady[LOCAL_CLIENT_READY], INFINITE );

			// Signal Client
			SetEvent( pSIB->hServerReady[LOCAL_SERVER_READY] );

//			msg( "Server gaat verder" );

		}
		else 
			SetEvent( pSIB->hServerReady[LOCAL_SERVER_READY] );

		*client_id = ClientId;
		*result = cs;
		return;
	}

	if( IS_CLIENT_KILLED(Client_i) ) {

		*client_id = pSIB->ProcessId[ INDEX( Client_i ) ];

		RemoveClient_from_ServerInfoBlock( pSIB, (INDEX( Client_i )) );


		cs = (CLEAN_STRING) rmalloc(sizeof(int) + 7 );
		cs->length = 7;
		//						  012345 6
		rsncopy( cs->characters, "Close\n\n", 7);
		*result = cs;

		//msg( "killed" );

		return; 

		//ExitProcess(-1);
	}

	if( IS_CLIENT_READY(Client_i) )
		;

#ifdef DEBUG
	msg( "client ready" );
#endif

	// Prepare communication channel
	b = (pSIB->hView[INDEX(Client_i)]) + (int) (pSIB->Offset[INDEX(Client_i)]);
	SetHandlesToClient(
		pSIB->hServerReady[INDEX(Client_i)],
		pSIB->hClientKilledOrReady[INDEX_CLIENT_KILLED(Client_i)],
		pSIB->hClientKilledOrReady[INDEX_CLIENT_READY(Client_i)] );


	length = *((DWORD *) (b+SIZE_OF_MESSAGE));

	ClientProcessId = *((DWORD *) (b+DATA_START));
	ClientProcessHandle = pSIB->hClientKilledOrReady[INDEX_CLIENT_KILLED(Client_i)];

	s = b+DATA_START+sizeof(DWORD);	

	cs = (CLEAN_STRING) rmalloc (sizeof(int) + /*rstrlen(s) + 1*/ length );
	rsncopy( cs->characters, s, length);
	cs->length = length;  rstrlen(s);

	(*result) = cs;
	*client_id = pSIB->ProcessId[ INDEX(Client_i) ];

	return;

}

void ReceiveCodeDataAdr(int code_size, int data_size, CLEAN_BOOL *result, int *code_start, int *data_start) {
	
//	char *i;

#ifdef DEBUG
	msg( "ReceiveCodeDataAdr" );
#endif

	if( state != PROCESS_REQUESTS ) {
		msg("ReceiveCodeDataAdr: no init");
		ExitProcess(-1);
	}

	// Reserve memory for server
	// Code
	if( code_size != 0 ) {
		CodeBuffer = (int *) VirtualAlloc( NULL,code_size,MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE );

		if( !CodeBuffer ) {
			error();
			msg( "**CodeBuffer");
			ExitProcess(-1);
		}
	}

	// Data
	if( data_size != 0) {
		DataBuffer = (int *) VirtualAlloc( NULL,data_size,MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE );

		if( (DataBuffer == NULL) && (data_size != 0)) {
			error();
			msg( "DataBuffer");
			ExitProcess(-1);
		}
	}

	// Make client reserve memory for code and data
	b[MESSAGE_TYPE] = ADDRESS_UNKNOWN;

	*((int *) (b+DATA_START)) = code_size;
	*((int *) (b+DATA_START+sizeof(int))) = data_size;

#ifdef DEBUG
	msg( "ReceiveCodeDataAdr 2" );
#endif

	Send();

#ifdef DEBUG
	msg( "ReceiveCodeDataAdr 3" );
#endif

	// Receive message containing startaddresses
	if( (Receive()) == CLIENT_READY ) {
		CodeSize = code_size;
		CodeStart = *((int *) (b+DATA_START));
		*code_start = CodeStart;

		DataSize = data_size;
		DataStart =	*((int *) (b+DATA_START+sizeof(int))); 
		*data_start = DataStart;

		// Operation correctly performed
		*result = CLEAN_TRUE;
	} else { 
		*result = CLEAN_FALSE;

		// Free used server buffers (code,data)
 		if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReceiveCodeDataAdr: 1" );
			ExitProcess(-1);
		}
		CodeBuffer = NULL;


		if( !VirtualFree(DataBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReceiveCodeDataAdr: 2" );
			ExitProcess(-1);
		}
		DataBuffer = NULL;
	}

#ifdef DEBUG
	msg( "ReceiveCodeDataAdr 4");
#endif

}

//#define DEBUG

/*
** NeedBaseLibraries
*/
void NeedBaseLibraries(CLEAN_STRING clstring, int n_libraries,CLEAN_BOOL *result,CLEAN_STRING *s) {

	char *hLibraryBufferView = NULL;
#ifdef CREATE_BUFFER2
	BufferInfo server_info;
	BufferInfo client_info;
#endif

#ifdef DEBUG
	msg( "NeedBaseLibraries 1" );
#endif

	if (n_libraries == 0) {
		*result = CLEAN_TRUE;
		return;
	}

//	__asm int 3

	/*
	** Allocate and fill buffer with library names
	*/

#ifdef DEBUG
	msg( "NeedBaseLibraries 2" );
#endif

	// Work						
#ifdef CREATE_BUFFER2
	CreateBuffer2( "ServerLibraryBuffer", clstring->length, FALSE, &server_info);
	hLibraryBufferView = server_info.hView;
#else
	hLibraryBufferView = (char *)CreateBuffer( "ServerLibraryBuffer", clstring->length, FALSE);
#endif 
	rsncopy(hLibraryBufferView, clstring->characters,clstring->length);

#ifdef DEBUG
	msg( "NeedBaseLibraries 3" );

	rscopy( hLibraryBufferView, "Hallo!" );

	sprintf( bs, "SERVER: <%s> <%d>",  hLibraryBufferView, clstring->length );
	msg( bs );
#endif 

	/*
	** Send a message demanding the bases of libraries
	*/
	b[MESSAGE_TYPE] = NEED_BASE_OF_LIBRARIES;
	*((int *) (b+DATA_START)) = n_libraries;
	*((int *) (b+DATA_START+sizeof(int))) = clstring->length;

#ifdef DEBUG
	rsprintf( bs, "SERVER: n_libraries: %d, string length: %d", n_libraries, clstring->length );
	msg( bs );
#endif

	Send();

#ifdef DEBUG
	msg( "NeedBaseLibraries 3a" );
#endif

	if( (Receive()) == CLIENT_READY ) {

#ifdef CREATE_BUFFER2
		CloseBuffer2( &server_info );
#else
		CloseBuffer( hLibraryBufferView );
#endif 

		/*
		** Answer received. Convert it to a string
		*/
#ifdef DEBUG
		msg( "NeedBaseLibraries 4" );
#endif

#ifdef CREATE_BUFFER2
		CreateBuffer2( "ClientLibraryBuffer",BUFFER_SIZE_UNKNOWN, TRUE, &client_info);
		hLibraryBufferView = client_info.hView;
#else	
		hLibraryBufferView = (char *) CreateBuffer( "ClientLibraryBuffer",BUFFER_SIZE_UNKNOWN, TRUE);
#endif

#ifdef DEBUG
		msg( "NeedBaseLibraries 5" );
#endif
		*result = CLEAN_TRUE;
		*s = cleanstringn(hLibraryBufferView,sizeof(int) * n_libraries);


#ifdef CREATE_BUFFER2
		CloseBuffer2( &client_info );
#else
		CloseBuffer( hLibraryBufferView );
#endif

	} else {
	
		*result = CLEAN_FALSE;
		*s = EmptyCleanString;
		msg( "NeedBaseLibraries: client killed" );

	}

}

int FlushBuffers() {
	DWORD lpNumberOfBytesWritten;

	if( (CodeBuffer != NULL) && (CodeSize !=0 ) ) {
		
		if( !WriteProcessMemory(ClientProcessHandle,
							(LPVOID) CodeStart, //code,
							CodeBuffer, // source,
							CodeSize,
							&lpNumberOfBytesWritten) ) {
			error();
			msg( "mwrites");
			ExitProcess(-1);
		}

		if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReplyReq: if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {" );
			ExitProcess(-1);
		}
		CodeBuffer = NULL;
	}

	if( (DataBuffer != NULL) && (DataSize != 0) ) {
		if( !WriteProcessMemory(ClientProcessHandle,
							(LPVOID) DataStart, //code,
							DataBuffer, // source,
							DataSize,
							NULL) ) {
			error();
			msg( "mwrites");
			ExitProcess(-1);
		}

		if( !VirtualFree(DataBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReplyReq: 1" );
			ExitProcess(-1);
		}
		DataBuffer = NULL;
	}

	return 1;
}



int ReplyReqS(CLEAN_STRING message)
{
	DWORD lpNumberOfBytesWritten;

#ifdef DEBUG
	msg( "ReplyReqS" );
#endif
//	__asm int 3



	if( state != PROCESS_REQUESTS ) {
		msg("ReplyReqS: no init");
		ExitProcess(-1);
	}

#ifdef DEBUG_MV
	if( (CodeBuffer != NULL) && (CodeSize !=0 ) ) {
		
		if( !WriteProcessMemory(ClientProcessHandle,
							(LPVOID) CodeStart, //code,
							CodeBuffer, // source,
							CodeSize,
							&lpNumberOfBytesWritten) ) {
			error();
			msg( "mwrites");
			ExitProcess(-1);
		}

		if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReplyReq: if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {" );
			ExitProcess(-1);
		}
		CodeBuffer = NULL;
	}

	if( (DataBuffer != NULL) && (DataSize != 0) ) {
		if( !WriteProcessMemory(ClientProcessHandle,
							(LPVOID) DataStart, //code,
							DataBuffer, // source,
							DataSize,
							NULL) ) {
			error();
			msg( "mwrites");
			ExitProcess(-1);
		}

		if( !VirtualFree(DataBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReplyReq: 1" );
			ExitProcess(-1);
		}
		DataBuffer = NULL;
	}
#endif



	b[MESSAGE_TYPE] = ADDRESS_KNOWN;
//	b[SIZE_OF_MESSAGE] = message->length;
	*((DWORD *) (b+SIZE_OF_MESSAGE)) = message->length;

	rsncopy( /**((char *) (b+DATA_START))*/ b + DATA_START,message->characters, message->length);

	Send();

#ifdef DEBUG
	msg( "returning from ReplyReqS" );
#endif

	return 1;
}

// -------
int ReplyReq(int num)
{
	DWORD lpNumberOfBytesWritten;
//	char s[100];

#ifdef DEBUG
	msg( "ReplyReq" );
#endif

//	sprintf( s, "code size: %d,  data size: %d", CodeSize, DataSize );
//	msg( s );


	if( state != PROCESS_REQUESTS ) {
		msg("ReplyReq: no init");
		ExitProcess(-1);
	}

	if( (CodeBuffer != NULL) && (CodeSize !=0 ) ) {
		
		if( !WriteProcessMemory(ClientProcessHandle,
							(LPVOID) CodeStart, //code,
							CodeBuffer, // source,
							CodeSize,
							&lpNumberOfBytesWritten) ) {
			error();
			msg( "mwrites");
			ExitProcess(-1);
		}

		if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReplyReq: if( !VirtualFree(CodeBuffer,0,MEM_RELEASE) ) {" );
			ExitProcess(-1);
		}
		CodeBuffer = NULL;
	}

	if( (DataBuffer != NULL) && (DataSize != 0) ) {
		if( !WriteProcessMemory(ClientProcessHandle,
							(LPVOID) DataStart, //code,
							DataBuffer, // source,
							DataSize,
							NULL) ) {
			error();
			msg( "mwrites");
			ExitProcess(-1);
		}

		if( !VirtualFree(DataBuffer,0,MEM_RELEASE) ) {
			error();
			msg( "ReplyReq: 1" );
			ExitProcess(-1);
		}
		DataBuffer = NULL;
	}

	b[MESSAGE_TYPE] = ADDRESS_KNOWN;
	*((int *) (b+DATA_START)) = num;
//	msg( "voor return");
	Send();

#ifdef DEBUG
	msg( "returning from ReplyReq" );
#endif

	return 1;
}

CLEAN_BOOL KillClient(int client_id) {

	int i;

	i = (N_OF_RESERVED_ENTRIES_INITIAL_BLOCK / 2);
	
	while( i < ((N_OF_RESERVED_ENTRIES_INITIAL_BLOCK / 2) + (pSIB->n_Clients)) )  {

		if( (pSIB->ProcessId)[i] == client_id ) {
			
			TerminateProcess( pSIB->hClientKilledOrReady[2 * i + CLIENT_KILLED], 0 );

			return( CLEAN_TRUE );
		}
		i++;
	}
	
	msg( "KillClient: internal error; could not kill client process" );
	ExitProcess(-1);
	return( CLEAN_FALSE );
}
