/* Agent Tcl
   Bob Gray
   10 January 1996

   genBasic.cc

   This file implements the basic agent routines.

   Copyright (c) 1995-1996, Bob Gray, Dartmouth College

   See the file "agent.terms" for information on usage and redistribution
   of this file and for a DISCLAIMER OF ALL WARRANTIES.
*/

#include "platPorting.h"	// platform-specific definitions
#include "platDelete.h"		// DELETE_OBJECT and DELETE_ARRAY_OBJECT
#include "mesgConnection.h"	// FileConnection
#include "genMesgDriver.h"	// MessageDriver


#include "platCharact.h"
#include "mesgTcpip.h"		// TCP/IP routines
#include "mesgMachId.h"	  	// MachineId
#include "agentPermit.h"	// Permit 
#include "genAgentIO.h"		// AgentIO
#include "suppDString.h"	// DynamicString
#include "suppDBuffer.h"	// DynamicBuffer
#include "genMasks.h"		// AgentMasks
#include "genServerInterface.h" // ServerInterface

#include "genAgent.h"
#include "genAgentConn.h"
#include "genEncrypt.h"
#include "genLocation.h"
#include "genManager.h"
#include "genMessage.h"
#include "genRestrict.h"
#include "genTransmit.h"
#include "genUtility.h"
#include "randpool.h"
#include "random.h"
#include "truefalse.h"

#ifdef FIX_LATER
    /* 
     * Remember to comment out the ALLOW_SAME_MACHINE_JUMPS #define when not
     * in testing mode.
     * 
     * UPDATE 3/27/99: I never remember to comment it out so clearly we need 
     * some kind of TESTING_MODE compilation flag (set in the Makefile) to 
     * control which other flags are set.
     */
#endif

#define ALLOW_SAME_MACHINE_JUMPS

/* AGENT::agentBegin

   Purpose: Register the agent with the server

     Input: time	= maxium time to wait for server contact
			  (struct timeval)

	    server	= name and IP address of the server
			  (class MachineId *)

    Output: The procedure returns a standard agent error code.
*/   
	    
int AGENT::agentBegin (struct timeval delay, const MachineId &machine)
{
    int code;               
    MachineId destMachine;

	/* agent might already registered */

    if (location -> isRegistered()) {
	return AGENT_ALREADY_REG;
    }

	/* register with the local machine if no machine is specified */

    if (machine.isEmpty()) {
	destMachine = location -> getMachine();
    } else {
	destMachine = machine;
    }

	/* add the permit */

    Permit permit;
    struct timeval stop;     
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* construct the server message */

    MESSAGE message (messagesToServer.messages[REQ_BEGIN]);
    message.addString (0, location -> machine.getName().value());
    message.addIp (1, location -> machine.getIp());
    message.addLong (2, (UINT_32) getpid ());

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr = 
	AgentConnectionProxy::makeConnection 
	    (stop, this, destMachine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_BAD);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL);

    } else {

	MESSAGE *response = NULL;
	AgentConnectionProxy &connection = *connectionPtr;

	if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

	} else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

	} else if (response -> getFlag() != RESP_BEGIN) {

	    if (response -> getFlag() == RESP_ERROR) {
		code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) (ServerInterface::ServerErrorcodes) response -> getByte (0));
	    } else {
		code = AGENT_SERVER_ERROR;
	    }

	} else {

	    UINT_32 randsize;

		/* break out the agent identification */

	    SmartIdPtr localId (response -> getId (0));

		/* stir in the random bytes (and seed the random number */
		/* generators in the standard C library)                */

	    SmartCharPtr randbytes (response -> getBinary (1, randsize));
	    randPoolAddBytes ((unsigned char *) ((char *) randbytes), randsize);
#ifndef NO_SRAND_FUNCTION
	    srand (agentRandom());
#endif
#ifndef NO_SRANDOM_FUNCTION
	    srandom (agentRandom());
#endif

		/* update location and security information */

	    location -> update (*localId);
	    security -> recordServerKeyname (*localId);

		/* turn on the background handler -- ownership of the  */
		/* connection is transferred to the background handler */ 

	    io -> turnBackgroundOn (connection.relinquishConnection());
 
		/* success */

	    code = AGENT_OK;
	}

	DELETE_OBJECT(response);
	DELETE_OBJECT(connectionPtr);
    }

	/* done */

    return (code);
}

/* AGENT::agentEnd

   Purpose: Remove the agent from the server tables

     Input: time	= maxium time to wait for server contact
			  (struct timeval)

	    endType     = e_END_NORMAL if the call to agentEnd was 
			  agent-initiated or e_END_CLEANUP if the call to
                          agentEnd was system-initiated due to some error
                          condition
                          (enum EndType)

    Output: The procedure returns one of the following codes.

	    AGENT_OK	       = success
	    AGENT_NOT_REG      = agent has not been registered
	    AGENT_NO_SERVER    = unable to contact the server
	    AGENT_BAD_RESPONSE = incomplete response from server
	    AGENT_SERVER_ERROR = error response from server 
	    AGENT_TIMEOUT      = timeout while attempting to contact server
*/

int AGENT::agentEnd (struct timeval delay, enum EndType endType)
{
    int code;			 /* return code              */
    struct timeval stop;         /* time at which to give up */
    MESSAGE *response = NULL;    /* response from the server */

	/* make sure that the agent is registered and is not a root agent */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* destination machine */

    MachineId machine ((char *) NULL, location -> localId.getServerIp());

	/* construct the server message */

    MESSAGE message (messagesToServer.messages[REQ_END]);
    message.addString (0, NULL);
    message.addLong (1, location -> localId.getId());

        /* connect and send the message */

    AgentConnectionProxy *connectionPtr = 
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code, 
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL);

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        code = connection -> RPC (message, messagesFromServer, response, NULL);

        if (code != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);
    
        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_ID) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0));
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* 
         * Cleanup and exit (if we are an agent that arrived via the server).
	 * Note that we can not "reset" the location data until after we
	 * have made the isServer test since "reset" empties out the LOCATION
	 * data.
         */

    if (code == AGENT_OK) {

        io -> turnBackgroundOff ();
	masks -> deleteAllIncomingItems ();
	meetings -> killAllMeetings ();
	security -> throwServerKeyname ();

	if (location -> isServer()) {
	    if (endType == e_END_NORMAL) {
		exit (0);
	    } else if (endType == e_END_NORMAL_BUT_NO_EXIT) {
		code = AGENT_EXIT;
	    }
	}

	location -> reset ();
    }

    return (code);
}

/* AGENT::agentName

   Purpose: Change the symbolic name of the agent

     Input: time = maxium amount of time to wait for server contact
		   (struct timeval)

	    name = new symbolic name for the agent
		   (char *)

    Output: The procedure returns one of the following codes.

	    AGENT_OK	       = success
	    AGENT_NOTREG       = agent is not registered with a server 
	    AGENT_NO_SERVER    = unable to contact the server
	    AGENT_BAD_RESPONSE = incomplete response from server
	    AGENT_SERVER_ERROR = error response from server 
*/

int AGENT::agentName (struct timeval delay, char *name)
{
    int code;                    /* return code              */
    struct timeval stop;         /* time at which to give up */
    MESSAGE *response = NULL;    /* server response          */

	/* assertions on the parameters */

    assert (name != NULL);

	/* make sure the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* destination machine */

    MachineId machine ((char *) NULL, location -> localId.getServerIp());

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_NAME]);
    message.addLong (0, location -> localId.getId());
    message.addString (1, name);

	/* connect and then send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code, 
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL);

    } else {

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag () == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte (0));
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    location -> updateName (name);
	    code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* cleanup */

    return (code); 
}

/* AGENT::agentRoot

   Purpose: Turn the agent into a root agent

     Input: None

    Output: The procedure returns one of the following codes.

	    AGENT_OK      = success
	    AGENT_NOT_REG = the agent is not registered
*/

int AGENT::agentRoot (void)
{
	/* check whether the agent has been registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* turn the agent into a root agent */

    location -> turnIntoRoot ();
    return AGENT_OK;
}

/* AGENT::agentSendMessage

   Purpose: Send a message to another agent

     Input: delay   = maxium time to wait for agent contact
		      (struct timeval)

	    id      = destination agent
		      (const AgentId &)

	    code    = the message code
		      (UINT_32)

	    string  = the message string
		      (const char *)

    Output: The procedure returns one of the following error codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent is not registered
	    AGENT_NO_SERVER    = unable to contact the server
	    AGENT_BAD_RESPONSE = no response from the server
	    AGENT_SERVER_ERROR = server error
	    AGENT_TIMEOUT      = timeout while attempting to contact the server   
*/

int AGENT::agentSendMessage (struct timeval delay, const AgentId &id, UINT_32 mcode, char *mstring)
{
    int code;                   /* return code              */
    MESSAGE *response = NULL;   /* message response         */
    struct timeval stop;        /* time at which to give up */

	/* assertions on the parameters */

    assert (mstring != NULL);

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* destination machine */

    MachineId machine (id.getServerName(), id.getServerIp());

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_MESSAGE]);
    message.addId (0, &location -> localId);
    message.addString (1, id.getName().value());
    message.addLong (2, id.getId());
    message.addLong (3, mcode);
    message.addString (4, mstring);

	/* connect to the server */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else {

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) { 

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0));
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	   code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* clean up */

    return (code);
}

/* AGENT::agentReceiveMeeting

   Purpose: Receive a meeting request that was sent from another agent

     Input: delay = maximum time to wait for incoming meeting request 
		    (struct timeval)

    Output: The procedure returns one of the following codes.

	    AGENT_OK              = success
	    AGENT_TIMEOUT         = timeout while waiting for a message
	    AGENT_LOST_BACKGROUND = background handler has failed

	    On success

	    message = pointer to a dynamcially allocated INCOMING_MEETING
		      (class INCOMING_MEETING *&)
*/

int AGENT::agentReceiveMeeting (struct timeval delay, INCOMING_MEETING *&meeting)
{
    int rc;                 /* return code from MASKS::getIncoming */

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* get the next available meeting request */

    rc = io -> receiveIncomingItem (MEETING_ITEM, (INCOMING *&) meeting, delay);
    return (rc);
}

/* AGENT::agentMeetingGetfile

   Purpose: Receive a file that is arriving on a meeting

     Input: delay     = maximum time to wait
		        (struct timeval)

	    handle    = meeting handle
		        (UINT_32)

	    callbacks = file access callbacks
			(class FileCallbacks &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK              = success
	    AGENT_NOT_REG         = agent has not been registered
	    AGENT_BAD_MEETING     = meeting does not exist
            AGENT_CANT_RECEIVE    = meeting is not connected
	    AGENT_NEXT_MESSAGE    = next thing is a *message* not a file 
	    AGENT_MEET_BROKEN     = meeting has been broken
	    AGENT_TIMEOUT         = timeout has expired
	    AGENT_LOST_BACKGROUND = lost the background connection
	    AGENT_CANT_WRITE      = unable to write to the file

	    On success

	    bytes  = number of bytes written to the file
		     (UINT_32)
*/ 
		  
int AGENT::agentMeetingGetfile (struct timeval delay, UINT_32 handle, FileCallbacks &callbacks, UINT_32 &bytes)
{
    int rc;     

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* get the next available message */

    rc = meetings -> receiveFile (delay, handle, callbacks, bytes);

    switch (rc) {

	case MEET_OK:

	    rc = AGENT_OK;
	    break;

	case MEET_NONEXISTENT:

	    rc = AGENT_BAD_MEETING;
	    break;

	case MEET_CANT_RECEIVE:

	    rc = AGENT_CANT_RECEIVE;
	    break;

	case MEET_LOST_CONNECTION:

	    rc = AGENT_MEET_BROKEN;
	    break;

	case MEET_TIMEOUT:

	    rc = AGENT_TIMEOUT;
	    break;

	case MEET_NEXT_MESSAGE:

	    rc = AGENT_NEXT_MESSAGE;
	    break;

	case MEET_CANT_WRITE:

	    rc = AGENT_CANT_WRITE;
	    break;

	default:

	    rc = AGENT_UNKNOWN;
    }
 
    return (rc);
}

/* AGENT::agentMeetingReceive

   Purpose: Receive a message that is arriving on a meeting

     Input: delay  = maximum time to wait
		     (struct timeval)

            handle = meeting handle
		     (UINT_32)

    Output: The procedure returns one of the following codes.

	    AGENT_OK              = success
	    AGENT_NOT_REG         = agent has not been registered
	    AGENT_BAD_MEETING     = meeting does not exist
            AGENT_CANT_RECEIVE    = meeting is not connected
	    AGENT_NEXT_FILE       = next thing is a *file* not a message
	    AGENT_MEET_BROKEN     = meeting has been broken
	    AGENT_TIMEOUT         = timeout has expired
	    AGENT_LOST_BACKGROUND = lost the background connection

	    On success
	
	    code   = message code
		     (UINT_32 &)

	    string = message string (dynamically allocated)
		     (char *&) 
*/

int AGENT::agentMeetingReceive (struct timeval delay, UINT_32 handle, UINT_32 &code, char *& string)
{
    int rc;     

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* get the next available message */

    rc = meetings -> receiveMessage (delay, handle, code, string);

    switch (rc) {

	case MEET_OK:

	    rc = AGENT_OK;
	    break;

	case MEET_NONEXISTENT:

	    rc = AGENT_BAD_MEETING;
	    break;

	case MEET_CANT_RECEIVE:

	    rc = AGENT_CANT_RECEIVE;
	    break;

	case MEET_LOST_CONNECTION:

	    rc = AGENT_MEET_BROKEN;
	    break;

	case MEET_TIMEOUT:

	    rc = AGENT_TIMEOUT;
	    break;

	case MEET_NEXT_FILE:

	    rc = AGENT_NEXT_FILE;
	    break;

	default:

	    rc = AGENT_UNKNOWN;
    }
 
    return (rc);
}

/* AGENT::agentReceiveMessage

   Purpose: Receive a message that was sent from another agent

     Input: delay = maxium time to wait for incoming message
		    (struct timeval)

    Output: The procedure returns one of the following codes.

	    AGENT_OK              = success
	    AGENT_TIMEOUT         = timeout while waiting for a message
	    AGENT_LOST_BACKGROUND = background handler has failed

	    On success

	    message = pointer to a dynamcially allocated INCOMING_MESSAGE
		      (class INCOMING_MESSAGE *&)
*/

int AGENT::agentReceiveMessage (struct timeval delay, INCOMING_MESSAGE *&message)
{
    int rc;                 /* return code from MASKS::getIncoming */

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* get the next available message */

    rc = io -> receiveIncomingItem (MESSAGE_ITEM, (INCOMING *&) message, delay);
    return (rc);
}

/* AGENT::agentSendEvent

   Purpose: Send an event to another agent

     Input: delay     = maxium time to wait for server contact
		        (struct timeval)

	    id        = the destination agent
		        (const AgentId &)

	    tag       = the event tag
			(char *)

	    string    = the event string
			(char *)

    Output: The procedure returns one of the following error codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent is not registered
	    AGENT_NO_SERVER    = unable to contact the server
	    AGENT_BAD_RESPONSE = no response from the server
	    AGENT_SERVER_ERROR = server error
	    AGENT_TIMEOUT      = timeout while attempting to contact the server   
*/

int AGENT::agentSendEvent (struct timeval delay, const AgentId &id, char *tag, char *string)
{
    int code;              /* return code              */
    MESSAGE *response;     /* message response         */
    struct timeval stop;   /* time at which to give up */

	/* assertions on the parameters */

    assert (tag != NULL);
    assert (string != NULL);

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* destination machine */

    MachineId machine (id.getServerName(), id.getServerIp());

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_EVENT]);
    message.addId (0, &location -> localId); 
    message.addString (1, id.getName().value());
    message.addLong (2, id.getId());
    message.addString (3, tag);
    message.addString (4, string);

	/* connect and send the message */ 

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL);

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0)); 
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* clean up */

    return (code);
}

/* AGENT::agentReceiveEvent

   Purpose: Receive an event that was sent from another agent

     Input: delay = maxium time to wait for incoming message
		    (struct timeval)

    Output: The procedure returns one of the following codes.

	    AGENT_OK              = success
	    AGENT_TIMEOUT         = timeout while waiting for a message
	    AGENT_LOST_BACKGROUND = background handler has failed

	    On success

	    event = pointer to a dynamcially allocated INCOMING_EVENT structure
		    (class INCOMING_EVENT *&)
*/

int AGENT::agentReceiveEvent (struct timeval delay, INCOMING_EVENT *&event)
{
    int rc;                 /* return code from MASKS::getIncoming */

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* get the next available message */

    rc = io -> receiveIncomingItem (EVENT_ITEM, (INCOMING *&) event, delay);
    return (rc);
}

/* AGENT::agentSleep 

   Purpose: Sleep for the specified amount of time 

     Input: delay = sleep time
		    (struct timeval)

    Output: The procedure returns AGENT_TIMEOUT if the sleep interval
	    expires.  Otherwise the procedure returns one of the agent
	    error codes. 
*/

int AGENT::agentSleep (struct timeval delay)
{
    return (io -> sleep (delay));
}

/* AGENT::agentForce

   Purpose: Forcibly terminate an agent

     Input: delay = maximum amount of time to wait for server contact
		    (struct timeval)

	    id    = identification of the agent to be forcibly terminated
		    (struct AgentId *)

    Output: The procedure returns one of the following codes.

	    AGENT_OK	= success

	    On success

	    forcedId = pointer to a dynamically allocated AgentId that
		       contains the full identification of the forced agent
		       (struct AgentId *&)
*/

int AGENT::agentForce (struct timeval delay, AgentId *id, AgentId *&forcedId)
{
    int sameIp;
    int returnCode;
    struct timeval stop;
    MESSAGE *response = NULL;
    UINT_32 localIp = location -> getLocalhostIp ();
    UINT_32 actualIp = location -> getMachineIp ();
 
	/* assertions on the parameters */

    assert (id != NULL);

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_FORCE]);
    message.addString (0, id -> getName().value());
    message.addLong (1, id -> getId());

	/* add the permit; removed automagically */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* make sure that we are not forcing ourself */

    TcpipErrors tcpipRc;
    MachineId machine (id -> getServerName(), id -> getServerIp());

    if ((tcpipRc = tcpip_getIP (machine, stop)) == e_TCPIP_TIMEOUT) {
	return AGENT_TIMEOUT;
    } else if (tcpipRc != e_TCPIP_OK) {
	return AGENT_NO_SERVER;
    }

    sameIp = ((machine.getIp() == localIp) || (machine.getIp() == actualIp));

    if (sameIp) {

	if (id -> getName().isEmpty()) {

	    if (id -> getId() == location -> localId.getId()) {
		return AGENT_SELF;
	    }

	} else {

	    if (id -> getName() == location -> localId.getName()) {
		return AGENT_SELF;
	    }
	}
    }

	/* connect and send the message */

    int messageRc;

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, messageRc,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	returnCode = messageCodeToAgentCode (messageRc, NULL); 

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        if ((messageRc = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    returnCode = messageCodeToAgentCode (messageRc, connectionPtr);
 
        } else if (response == NULL) {

	    returnCode = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_ID) {

	    if (response -> getFlag() == RESP_ERROR) {
	        returnCode = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0)); 
	    } else {
	        returnCode = AGENT_SERVER_ERROR;
	    }

        } else {

	    forcedId = response -> getId (0);     // don't deallocate
	    returnCode = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

    return (returnCode);
}

/* AGENT::agentGetIds

   Purpose: Get the numeric id of every agent registered with a server

     Input: delay   = maxium time to wait for sever contact
		      (struct timeval)

	    machine = server identification
		      (class MachineId &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent is not registered
	    AGENT_TIMEOUT      = timeout while attempting to contact server
	    AGENT_NO_SERVER    = specified server does not exist
	    AGENT_BAD_RESPONSE = server did not response
	    AGENT_SERVER_ERROR = error on the server side

	    On success

	    list = pointer to a dynamically allocated ID_LIST
		   (class ID_LIST *&)
*/

int AGENT::agentGetIds (struct timeval delay, const MachineId &machine, ID_LIST *&list)
{
    int code;
    AgentId *id;

	/* call the generic routine */

    id = new AgentId (machine.getName().value(), machine.getIp(), (char *) NULL, 0);
    code = agentGenericInfo (INFO_ALL_IDS, delay, id, (void *&) list);
    DELETE_OBJECT(id);
    return (code);
}

/* AGENT::agentGetInfo

   Purpose: Get information about a particular agent

     Input: delay = maximum time to wait for server contact
		    (struct timeval)

	    id    = agent identification
		    (struct AgentId *id)

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent is not registered
	    AGENT_TIMEOUT      = timeout while attempting to contact server
	    AGENT_NO_SERVER    = specified server does not exist
	    AGENT_BAD_RESPONSE = server did not response
	    AGENT_SERVER_ERROR = error on the server side
	    AGENT_NONEXISTENT  = specified agent does not exist

	    On success

	    info = pointer to a dynamically allocated AGENT_INFO structure
		   (struct AGENT_INFO *)
*/

int AGENT::agentGetInfo (struct timeval delay, AgentId *id, AGENT_INFO *& info)
{
    int code;

	/* assertions on the parameters */

    assert (id != NULL);

	/* call the generic routine */

    code = agentGenericInfo (INFO_ID, delay, id, (void *&) info);
    return (code);
}

/* AGENT::agentGetNames

   Purpose: Get the symbolic name of every agent registered with a server

     Input: delay   = maximum time to wait for server contact
		      (struct timeval)

	    machine = server identification
		      (class MachineId &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent is not registered
	    AGENT_TIMEOUT      = timeout while attempting to contact server
	    AGENT_NO_SERVER    = specified server does not exist
	    AGENT_BAD_RESPONSE = server did not response
	    AGENT_SERVER_ERROR = error on the server side

	    On success

	    list = pointer to a dynamically allocated NAME_LIST
		   (class NAME_LIST *&)
*/

int AGENT::agentGetNames (struct timeval delay, const MachineId &machine, NAME_LIST *&list)
{ 
    int code;
    AgentId *id;

	/* call the generic routine */

    id = new AgentId (machine.getName().value(), machine.getIp(), (char *) NULL, 0);
    code = agentGenericInfo (INFO_ALL_NAMES, delay, id, (void *&) list);
    DELETE_OBJECT(id);
    return (code);
}

/* AGENT::agentGenericInfo

   Purpose: Get the information about all the agents on the server or
	    information on a particular agent

     Input: flag    = INFO_ALL_IDS, INFO_ALL_NAMES or INFO_ID
		      (UINT_8)

	    delay   = maxium time to wait for server contact
		      (struct timeval)

	    id      = agent identification
		      (struct AgentId *)

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent is not registered
	    AGENT_TIMEOUT      = timeout while attempting to contact server
	    AGENT_NO_SERVER    = specified server does not exist
	    AGENT_BAD_RESPONSE = server did not response
	    AGENT_SERVER_ERROR = error on the server side
	    AGENT_NONEXISTENT  = specified agent does not exist

	    On success

            list = pointer to a dynamically allocated ID_LIST if flag was 
		   INFO_ALL_IDS
		   (void *& ==> ID_LIST *&)

	         = pointer to a dyanmically allocated NAME_LIST if flag was
		   INFO_ALL_NAMES
		   (void *& ==> NAME_LIST *&) 

		 = pointer to a dynamically allocated AGENT_INFO structure if
		   flag was INFO_ID  
		   (void *& ==> AGENT_INFO *&)
*/
 
int AGENT::agentGenericInfo (UINT_8 flag, struct timeval delay, AgentId *id, void *& list)
{
    int code;
    int responseFlag;
    struct timeval stop;
    MESSAGE *response = NULL;

	/* assertions on the parameters */

    assert ((flag == INFO_ALL_IDS) || (flag == INFO_ALL_NAMES) || (flag == INFO_ID));
    assert (id != NULL);

	/* response flag */

    responseFlag = (flag == INFO_ALL_IDS) ? RESP_IDS :
		   (flag == INFO_ALL_NAMES) ? RESP_NAMES : RESP_INFO;

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* destination machine */

    MachineId machine (id -> getServerName(), id -> getServerIp());

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_INFO]);
    message.addByte (0, flag);
    message.addString (1, id -> getName().value());
    message.addLong (2, id -> getId());

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr); 

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE; 

        } else if (response -> getFlag() != responseFlag) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte (0)); 
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else if (responseFlag == RESP_IDS) {

	    list = (void *) response -> getIds (0);      // don't deallocate
	    code = AGENT_OK;

        } else if (responseFlag == RESP_NAMES) {

	    list = (void *) response -> getNames (0);    // don't deallocate
	    code = AGENT_OK;

        } else {

	        /* break apart the message */

	    SmartCharPtr actual     (response -> getString (0));
	    UINT_32 actual_ip       = response -> getIp (1); 
            SmartIdPtr id           (response -> getId (2));
	    BASE_SECURITY *l_security = response -> getSecurity (3);
    
	        /* construct the AGENT_INFO structure */

	    DynamicString machineName (actual);

	    AGENT_INFO *info   = new AGENT_INFO;
	    info -> id         = *id; 
	    info -> actual     = MachineId (machineName, actual_ip);
	    info -> security   = *l_security;
	    info -> agentPid   = response -> getLong (4); 
	    info -> blockerPid = response -> getLong (5); 
	    info -> messages   = response -> getLong (6); 
	    info -> meetings   = response -> getLong (7); 
	    info -> events     = response -> getLong (8);
	    list               = (void *) info;
	    code               = AGENT_OK;

	        /* cleanup */

	    DELETE_OBJECT(l_security);
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* cleanup */

    return (code);
} 

/* AGENT::agentStatus

   Purpose: Check for various conditions such as whether an agent exists

     Input: delay        = maximum time to wait for server contact
		           (struct timeval)

	    type         = STATUS_EXISTS
		           (int)

	    provideEvent = 1 if we want the server to send a notification
			   event when the agent comes into existence
			   (int)

	    id           = agent identification
			   (class AgentId *)

    Output: The procedure returns one of the following codes.

	    AGENT_OK            = agent exists
	    AGENT_NOT_REG       = agent has not been registered
	    AGENT_TIMEOUT       = timeout while attempting to connect to server
	    AGENT_NO_SERVER     = unable to connect to the server
	    AGENT_BAD_RESPONSE  = bad response from the server
	    AGENT_PENDING_EVENT = agent does not exist but the server will
				  send a notification event 
            AGENT_NONEXISTENT   = agent does not exist and the user did not
				  request a notification event
	    AGENT_NO_EVENT      = agent does not exist but a notification 
				  event is impossible because there was no
				  symbolic name

	    The procedure can also return one of the general server error
	    codes.

	    AGENT_SERVER_LOST    = server has lost the agent
	    AGENT_SERVER_INVALID = server does not support status requests
	    AGENT_SERVER_SYSTEM  = system error on the server side 
	    AGENT_SERVER_ERROR   = unknown server error
*/

int AGENT::agentStatus (struct timeval delay, int type, int provideEvent, AgentId *id)
{
    int code;
    struct timeval stop;

	/* assertions on the parameters */

    assert (type == STATUS_EXISTS);
    assert (id != NULL);

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* destination machine */

    MachineId machine (id -> getServerName(), id -> getServerIp());

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_STATUS]);
    message.addByte (0, type);
    message.addByte (1, provideEvent ? STATUS_EVENT : STATUS_NO_EVENT);
    message.addString (2, id -> getName().value());
    message.addLong (3, id -> getId());
    message.addLong (4, location -> localId.getId());

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else {

	MESSAGE *response = NULL;
        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr); 

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0));
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else { 

	    code = provideEvent ? AGENT_PENDING_EVENT : AGENT_OK;
        } 

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* cleanup */

    return (code);
} 

/* AGENT::agentSubmit

   Purpose: Submit an agent

     Input: delay   = maximum time to wait for server contact
		      (struct timeval)

	    machine = destination machine
		      (class MachineId &)

	    version = version information for the required interpreter
		      (struct Version *)

	    script  = new agent
		      (class DynamicBuffer &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK            = success
 	    AGENT_NOT_REG       = agent is not registered
	    AGENT_TIMEOUT       = timeout while attempting to contact server
	    AGENT_NO_SERVER     = unable to find server's machine
	    AGENT_BAD_RESPONSE  = bad response from the server
	    AGENT_SERVER_ERROR  = unknown server error
	    AGENT_SERVER_SOURCE = server refused the request
	    AGENT_SERVER_INTERP = interpreter is not available at the server   

	    On success

	    id = pointer to a dyanmically allocated AgentId
		 (struct AgentId *&)
*/

int AGENT::agentSubmit (struct timeval delay, const MachineId &machine, Version *version, const DynamicBuffer &state, AgentId *&id)
{
    int code;
    struct timeval stop; 
    MESSAGE *response = NULL;

	/* make sure the the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* make sure that another child is allowed */

    if (!(restrict -> isChildAllowed())) {
	return AGENT_NO_CHILDREN;
    }

	/* use the current version information if none is specified */

    if (version == NULL) {
	version = &interpreterVersion;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* get the restrictions */

    RestrictPacker packer (*restrict, ServerInterface::e_SUB_SCRIPT);
    UINT_32 restrictSize = packer.getPackedSize();
    char *restrictBuffer = new char [restrictSize];
    SmartCharPtr restrictBufferMgr (restrictBuffer);    // ensure deletion
    (void) packer.pack (restrictBuffer);

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_SUBMIT]);
    message.addByte (0, ServerInterface::e_SUB_SCRIPT);
    message.addId (1, &location -> rootId);
    message.addId (2, &location -> rootHomeId);
    message.addId (3, &location -> localId);
    message.addId (4, &location -> homeId);
    message.addString (5, version -> m_name.value());
    message.addBinary (6, state.size(), state.value());
    message.addBinary (7, restrictSize, restrictBuffer);
    message.addBinary (8, 0, NULL);

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else {

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_ID) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte (0));  
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    restrict-> incrementTotalChildren ();
	    id = response -> getId (0);       // don't delete
	    code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

    return (code);
}

/* AGENT::agentAgletJump (currently used in Agent Java only)

   Purpose: Do Aglet-style migration.  This style of migration is different from
            jump in that the agent specifies a function to call, and execution
            begins on the remote machine from the top of that function.

     Input: delay   = maximum time to wait for server contact
		      (struct timeval)

	    machine = destination machine
		      (class MachineId &)

	    state   = java agent's object state
	              (class DynamicBuffer &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK            = success
 	    AGENT_NOT_REG       = agent is not registered
	    AGENT_TIMEOUT       = timeout while attempting to contact server
	    AGENT_NO_SERVER     = unable to find server's machine
	    AGENT_BAD_RESPONSE  = bad response from the server
	    AGENT_SERVER_ERROR  = unknown server error
	    AGENT_SERVER_SOURCE = server refused the request
	    AGENT_SERVER_INTERP = interpreter is not available at the server   

	    On success

	    id = pointer to a dyanmically allocated AgentId
		 (struct AgentId *&)
*/

int AGENT::agentAgletJump (struct timeval delay, const MachineId &machine,
			   const DynamicBuffer &state, AgentId *&id)
{
  int code;
  struct timeval stop;
  MESSAGE *response = NULL;
  
  /* make sure that the agent is registered */
  
  if (!(location -> isRegistered())) {
    return AGENT_NOT_REG;
  }
  
  /* make sure that migration is allowed */
    
  if (!(restrict -> isJumpAllowed())) {
    return AGENT_NO_JUMPS;
  }
  
  /* add the permit */
  
  Permit permit;
  permit.setWallLimit (delay);
  RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
  restrict -> getWallLimit (stop);
  
  /* normally we would test for jumps to the same machine here, but we skip the
     test so that the agent will start from its entrypoint. */

  /* get the masks */
  
  AgentMasksPacker masksPacker (*masks);
  UINT_32 masksSize = masksPacker.getPackedSize();
  char *masksBuffer = new char [masksSize];
  SmartCharPtr masksBufferMgr (masksBuffer);		// ensure deletion
  (void) masksPacker.pack (masksBuffer);
  
  /* get the restrictions */
  
  RestrictPacker restrictPacker (*restrict, ServerInterface::e_SUB_AGLET_JUMP);
  UINT_32 restrictSize = restrictPacker.getPackedSize();
  char *restrictBuffer = new char [restrictSize];
  SmartCharPtr restrictBufferMgr (restrictBuffer);     // ensure deletion
  (void) restrictPacker.pack (restrictBuffer);
  
  /* construct the message */
  
  MESSAGE message (messagesToServer.messages[REQ_SUBMIT]);
  message.addByte (0, ServerInterface::e_SUB_AGLET_JUMP);
  message.addId (1, &location -> rootId);
  message.addId (2, &location -> rootHomeId);
  message.addId (3, &location -> localId);
  message.addId (4, &location -> homeId);
  message.addString (5, interpreterVersion.m_name.value());
  message.addBinary (6, state.size(), state.value());
  message.addBinary (7, restrictSize, restrictBuffer);
  message.addBinary (8, masksSize, masksBuffer);
  
  /* connect and send the message */
  
  AgentConnectionProxy *connectionPtr =
    AgentConnectionProxy::makeConnection 
    (stop, this, machine, *security, code,
     AgentConnectionProxy::e_BACKGROUND_OKAY);
  
  if (connectionPtr == NULL) {
    
    code = messageCodeToAgentCode (code, NULL); 
    
  } else {
    
    AgentConnectionProxy &connection = *connectionPtr;
    
    if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {
      
      code = messageCodeToAgentCode (code, connectionPtr); 
      
    } else if (response == NULL) {
      
      code = AGENT_BAD_RESPONSE;
      
    } else if (response -> getFlag() != RESP_ID) {
      
      if (response -> getFlag() == RESP_ERROR) {
	code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes)
				      response -> getByte(0)); 
      } else {
	code = AGENT_SERVER_ERROR;
      }
      
    } else {
      
      id = response -> getId (0);        // don't delete
      code = AGENT_OK;
    }
    
    DELETE_OBJECT(connectionPtr);
    DELETE_OBJECT(response);
  }
  
  /* cleanup */
  
  return (code);
}

/* AGENT::agentAgletSubmit (currently used in Agent Java only)

   Purpose: Do an aglet-style submit.  For now, this procedure does exactly what
            agentSubmit does, but this may change when we allow agentAgletSubmit
            to use an arbitrary method name

     Input: delay   = maximum time to wait for server contact
		      (struct timeval)

	    machine = destination machine
		      (class MachineId &)

	    version = version information for the required interpreter
		      (struct Version *)

	    script  = new agent
		      (class DynamicBuffer &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK            = success
 	    AGENT_NOT_REG       = agent is not registered
	    AGENT_TIMEOUT       = timeout while attempting to contact server
	    AGENT_NO_SERVER     = unable to find server's machine
	    AGENT_BAD_RESPONSE  = bad response from the server
	    AGENT_SERVER_ERROR  = unknown server error
	    AGENT_SERVER_SOURCE = server refused the request
	    AGENT_SERVER_INTERP = interpreter is not available at the server   

	    On success

	    id = pointer to a dyanmically allocated AgentId
		 (struct AgentId *&)
*/

int AGENT::agentAgletSubmit (struct timeval delay, const MachineId &machine, 
			     Version *version, const DynamicBuffer &state, 
			     AgentId *&id)
{
    int code;
    struct timeval stop; 
    MESSAGE *response = NULL;

	/* make sure the the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* make sure that another child is allowed */

    if (!(restrict -> isChildAllowed())) {
	return AGENT_NO_CHILDREN;
    }

	/* use the current version information if none is specified */

    if (version == NULL) {
	version = &interpreterVersion;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* get the restrictions */

    RestrictPacker packer (*restrict, ServerInterface::e_SUB_AGLET_SUBMIT);
    UINT_32 restrictSize = packer.getPackedSize();
    char *restrictBuffer = new char [restrictSize];
    SmartCharPtr restrictBufferMgr (restrictBuffer);    // ensure deletion
    (void) packer.pack (restrictBuffer);

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_SUBMIT]);
    message.addByte (0, ServerInterface::e_SUB_AGLET_SUBMIT);
    message.addId (1, &location -> rootId);
    message.addId (2, &location -> rootHomeId);
    message.addId (3, &location -> localId);
    message.addId (4, &location -> homeId);
    message.addString (5, version -> m_name.value());
    message.addBinary (6, state.size(), state.value());
    message.addBinary (7, restrictSize, restrictBuffer);
    message.addBinary (8, 0, NULL);

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else {

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_ID) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte (0));  
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    restrict-> incrementTotalChildren ();
	    id = response -> getId (0);       // don't delete
	    code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

    return (code);
}

/* AGENT::agentTransfer

   Purpose: Transfer a state image from a disk file to a server

     Input: delay     = maximum time to wait for server contact
		        (struct timeval)

	    machine   = destination server
		        (class MachineId &)

	    callbacks = file access callbacks
			(class FileCallbacks &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK        = success
	    AGENT_CANT_READ = unable to read from file

	    On success

	    id = dynamically allocated AgentId structure that contains
		 the identification for the new agent -- i.e. the agent
		 that came into existence when the state image was
		 transferred to the server.
*/

int AGENT::agentTransfer (struct timeval delay, const MachineId &machine, FileCallbacks &callbacks, AgentId *&id)
{
    int code;
    struct timeval stop;
    MESSAGE *response = NULL;
    BOOLEAN twoMessages = e_FALSE;

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* get the state image out of the file */

    DynamicBuffer buffer;
    DynamicBuffer secondBuffer;
    FileConnection fileConnection (callbacks);
    code = MessageDriver::receiveIntoBuffer (fileConnection, buffer);

    if (code != MESSAGE_OK) {
	return (messageCodeToAgentCode (code, NULL));
    } 

    if (*buffer.value() == PROT_KEY) {

	code = MessageDriver::receiveIntoBuffer (fileConnection, secondBuffer);

	if (code != MESSAGE_OK) {
	    return (messageCodeToAgentCode (code, NULL));
	}

	twoMessages = e_TRUE;
    }

	/* connect to the server and send the state image */

#ifdef FIX_LATER
	/* 
	 * The message needs to be encrypted appropriately during transit!
	 */
#endif

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code, 
	     AgentConnectionProxy::e_BACKGROUND_BAD);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> unsecureSendFromBuffer (buffer)) != AGENT_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else if (!twoMessages) {

	    code = AGENT_OK;

        } else if ((code = connection -> unsecureSendFromBuffer (secondBuffer)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr);

        } else {

	    code = AGENT_OK; 
        }

	    /* receive the server response */

        if (code != AGENT_OK) {

	    // empty
	
        } else if (connection -> unsecureReceiveNoncomm (response, messagesFromServer) != MESSAGE_OK) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_ID) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0)); 
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    id = response -> getId (0);       // don't delete
            code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }
 
	/* cleanup */

    return (code);
}

/* AGENT::agentDisk

   Purpose: Write the state image  to a disk file

     Input: callbacks = read and write callbacks for the file
			(class FileCallbacks &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK         = success
 	    AGENT_NOT_REG    = agent is not registered
	    AGENT_BAD_STATE  = unable to capture agent state
	    AGENT_CANT_WRITE = file access error
*/

int AGENT::agentDisk (FileCallbacks &callbacks)
{
    int code;
    DynamicBuffer state;

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* make sure that migration is allowed */

    if (!(restrict -> isJumpAllowed())) {
	return AGENT_NO_JUMPS;
    }

	/* get the masks */

    AgentMasksPacker masksPacker (*masks);
    UINT_32 masksSize = masksPacker.getPackedSize();
    char *masksBuffer = new char [masksSize];
    SmartCharPtr masksBufferMgr (masksBuffer);		// ensure deletion
    (void) masksPacker.pack (masksBuffer);

	/* get the restrictions */

    RestrictPacker packer (*restrict, ServerInterface::e_SUB_JUMP);
    UINT_32 restrictSize = packer.getPackedSize();
    char *restrictBuffer = new char [restrictSize];
    SmartCharPtr restrictBufferMgr (restrictBuffer);    // ensure deletion
    (void) packer.pack (restrictBuffer);

	/* get the state information -- FALSE means we are not forking */ 

    if (captureState (AGENT::e_JUMPING, state) != AGENT_OK) {
	return AGENT_BAD_STATE;
    }

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_SUBMIT]);
    message.addByte (0, ServerInterface::e_SUB_JUMP);
    message.addId (1, &location -> rootId);
    message.addId (2, &location -> rootHomeId);
    message.addId (3, &location -> localId);
    message.addId (4, &location -> homeId);
    message.addString (5, interpreterVersion.m_name.value());
    message.addBinary (6, state.size(), state.value());
    message.addBinary (7, restrictSize, restrictBuffer);
    message.addBinary (8, masksSize, masksBuffer);

	/* dump to disk */

#ifdef FIX_LATER
	/*
	 * We use a dummy machine for now -- i.e., the state image will
	 * not be encrypted on disk but instead will only be signed
	 * with the owner's key.  See also the comment in agentTransfer 
         * above (where the disk image is transferred as-is, namely 
	 * unencrypted).  The current difficulty is that we will probably
	 * want to encrypt with different keys depending on whether it's
	 * on disk or in transit, which is awkward to do until we clean-up
	 * our encryption and security classes.
	 */
#endif

    MachineId machine;
    FileConnection connection (callbacks);
    code = MessageDriver::secureSend (connection, machine, *security, message);
    return (messageCodeToAgentCode (code, NULL));
}

/* AGENT::agentJump

   Purpose: Migrate or fork a copy of the current agent

     Input: delay    = maximum time to wait for server contact
		       (struct timeval)

	    machine  = destination machine
		       (class MachineId &)

	    jumpType = e_FORKING or e_JUMPING
		       (AGENT::jumpType)

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
 	    AGENT_NOT_REG      = agent is not registered
	    AGENT_TIMEOUT      = timeout while attempting to contact server
	    AGENT_NO_SERVER    = unable to find server's machine
	    AGENT_BAD_RESPONSE = bad response from the server
	    AGENT_SERVER_ERROR = error at the server
	    AGENT_BAD_STATE    = unable to capture agent state
	    AGENT_SAME	       = jumping to same machine

	    On success

	    id = pointer to a dyanmically allocated AgentId
		 (struct AgentId *&)
*/

int AGENT::agentJump (struct timeval delay, const MachineId &machine, AgentId *&id, enum JumpType jumpType)
{
    int code;
    DynamicBuffer state;
    struct timeval stop;
    MESSAGE *response = NULL;
    ServerInterface::SubmissionTypes type;

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* make sure that forking or migration is allowed */

    if (jumpType == e_FORKING) {

	if (!(restrict -> isChildAllowed())) {
	    return AGENT_NO_CHILDREN;
	}
   
    } else {

	if (!(restrict -> isJumpAllowed())) {
	    return AGENT_NO_JUMPS;
	}
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* don't jump to the same machine */

    if (jumpType == e_FORKING) {

	type = ServerInterface::e_SUB_FORK;

    } else {

#if defined(ALLOW_SAME_MACHINE_JUMPS)

	// skip the test

#elif defined(MEASURE_STATE_CAPTURE)

 	// skip the test

#else

	MachineId scratchId (machine);

	if (scratchId.getIp() == UNKNOWN_IP) {

	    TcpipErrors tcpipRc;

	    if ((tcpipRc = tcpip_getIP (scratchId, stop)) == e_TCPIP_TIMEOUT) {
		return AGENT_TIMEOUT;
	    } else if (tcpipRc != e_TCPIP_OK) {
		return AGENT_NO_SERVER;
	    }
	}

	if ((scratchId.getIp() == location -> getMachineIp()) || (scratchId.getIp() == location -> getLocalhostIp())) {
	    return AGENT_SAME;
	}

#endif

	type = ServerInterface::e_SUB_JUMP;
    }

	/* get the state information */

#if defined(MEASURE_STATE_CAPTURE)

    struct timeval startTime = SystemUtil::getCurrentWall();

    for (int i = 0; i < STATE_CAPTURE_ITERATIONS; ++i) {

        if (captureState (jumpType, state) != AGENT_OK) {
	    return AGENT_BAD_STATE;
        }

	state.empty();
    }

    struct timeval endTime = SystemUtil::getCurrentWall();
    struct timeval diff = TimevalUtil::subTimevals (endTime, startTime);
    printf ("start ==> %d %d\n", startTime.tv_sec, startTime.tv_usec);
    printf ("end ==> %d %d\n", endTime.tv_sec, endTime.tv_usec);
    printf ("diff ==> %d %d\n", diff.tv_sec, diff.tv_usec);
    exit (1);

#else

    if (captureState (jumpType, state) != AGENT_OK) {
	return AGENT_BAD_STATE;
    }

#endif

	/* get the masks */

    AgentMasksPacker masksPacker (*masks);
    UINT_32 masksSize = masksPacker.getPackedSize();
    char *masksBuffer = new char [masksSize];
    SmartCharPtr masksBufferMgr (masksBuffer);		// ensure deletion
    (void) masksPacker.pack (masksBuffer);

	/* get the restrictions */

    RestrictPacker restrictPacker (*restrict, type);
    UINT_32 restrictSize = restrictPacker.getPackedSize();
    char *restrictBuffer = new char [restrictSize];
    SmartCharPtr restrictBufferMgr (restrictBuffer);    // ensure deletion
    (void) restrictPacker.pack (restrictBuffer);

	/* construct the message */

    MESSAGE message (messagesToServer.messages[REQ_SUBMIT]);
    message.addByte (0, type);
    message.addId (1, &location -> rootId);
    message.addId (2, &location -> rootHomeId);
    message.addId (3, &location -> localId);
    message.addId (4, &location -> homeId);
    message.addString (5, interpreterVersion.m_name.value());
    message.addBinary (6, state.size(), state.value());
    message.addBinary (7, restrictSize, restrictBuffer);
    message.addBinary (8, masksSize, masksBuffer);

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL); 

    } else {

        AgentConnectionProxy &connection = *connectionPtr;

        if ((code = connection -> RPC (message, messagesFromServer, response, &stop)) != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr); 

        } else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_ID) {

	    if (response -> getFlag() == RESP_ERROR) {
	        code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0)); 
	    } else {
	        code = AGENT_SERVER_ERROR;
	    }

        } else {

	    if (jumpType == e_FORKING) {
	        restrict -> incrementTotalChildren ();
	    }
   
	    id = response -> getId (0);        // don't delete
            code = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }
 
	/* cleanup */

    return (code);
}

/* AGENT::agentIp 

   Purpose: Get the IP address of the current machine from the agent server

     Input: timerval = maximum server delay
                       (struct timeval) 

    Output: The procedure returns one of the following codes.

	    AGENT_OK    = success

	    On success the procedure fills in "machine" with the host name
	    and ip address.
*/

int AGENT::agentIp (struct timeval delay, MachineId &machine)
{
    int code;

	/* add the permit */

    Permit permit;
    struct timeval stop;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* IP message and the destination machine (which is just localhost)  */
 
    MESSAGE message (messagesToServer.messages[REQ_IP]);
    MachineId localMachine (NULL, location -> getLocalhostIp());

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection
	    (stop, this, localMachine, *security, code,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
   
    if (connectionPtr == NULL) {

	code = messageCodeToAgentCode (code, NULL);

    } else { 

        MESSAGE *response = NULL;
        AgentConnectionProxy &connection = *connectionPtr;

	code = connection -> unsecureRPC (message, messagesFromServer, response);

	if (code != MESSAGE_OK) {

	    code = messageCodeToAgentCode (code, connectionPtr); 

	} else if (response == NULL) {

	    code = AGENT_BAD_RESPONSE;

	} else if (response -> getFlag() != RESP_IP) {

	    if (response -> getFlag() == RESP_ERROR) {
		code = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0));
	    } else {
		code = AGENT_SERVER_ERROR;
	    }

	} else {

	    char *charPtr = response->getString(0);
	    SmartCharPtr actualMachine (charPtr);
	    UINT_32 actualIp           = response -> getIp (1);

		/* update the machine */

	    machine.setName (DynamicString (actualMachine));
	    machine.setIp (actualIp);
	    code = AGENT_OK;
        }

	DELETE_OBJECT(response);
	DELETE_OBJECT(connectionPtr);
    }

	/* done */ 

    return (code);
}

/* AGENT::agentAccept

   Purpose: Accept a meeting (and wait for the other agent to connect back
	    to us)

     Input: delay  = maximum time to wait for meeting completion
		     (struct timeval)

	    handle = handle of the meeting
		     (UINT_32)

    Output: The procedure returns a standard agent error code if the meeting
 	    is broken or some other error occurs.  Otherwise the procedure
            returns AGENT_OK.  As soon as possible, the procedure sets

	    id = identification of the agent on the other side of the meeting
		 (for error reporting purposes)
		 (AgentId &)
*/

int AGENT::agentAccept (struct timeval delay, UINT_32 handle, AgentId &id)
{
    int code;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);

	/* mutual exclusion -- automatically released */

    Guard guard (monitor);

	/* accept the meeting */

#ifdef FIX_LATER
	    /* we are doing extra work since agentAcceptMeeting adds */
	    /* another permit to the set of Restrictions             */
#endif

    if ((code = agentAcceptMeeting (delay, handle, id)) != AGENT_OK) {
	return (code);
    }
    
	/* get the meeting instance */

    MEETING *meeting = meetings -> lookup_noLock (handle);

	/* wait until the meeting is rejected, broken or complete */

    while (1) {		// something to break out of

	UINT_8 meetingStatus;

#ifdef FIX_LATER
	    /* we are once again doing extra work since simpleSelect_noLock */
	    /* adds yet another permit to the set of Restrictions           */
#endif

	if ((code = io -> simpleSelect_noLock (*meeting, meetingStatus, delay)) != AGENT_OK) {
	    break;
	}

	if (meetingStatus == MEET_COMPLETE) {
	    code = AGENT_OK;
	    break;
	}

	if ((meetingStatus == MEET_CONNECTING) || (meetingStatus == MEET_ACCEPTING)) {
	    continue;
	}

	code = AGENT_MEET_BROKEN;
	break;
    }

	/* done */

    return (code);
}

/* AGENT::agentMeet

   Purpose: Meet with another agent (i.e., combines requestMeeting and
            simpleSelect such that it does not return until the meeting has
	    been rejected, has been broken, or has been completed).

     Input: delay = maximum time to wait for meeting completion
		    (struct timeval)

	    id    = identification of the other agent
		    (const AgentId &)

    Output: The procedure returns a standard agent error code if the meeting
            is rejected or broken or some other error occurs.  The procedure
	    returns AGENT_OK on success.

	    On success 

	    handle = handle of the new meeting
	             (UINT_32)
*/

int AGENT::agentMeet (struct timeval delay, const AgentId &id, UINT_32 &handle)
{
    int code;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);

	/* mutual exclusion -- automatically released */

    Guard guard (monitor);

	/* collect any incoming meeting requests */

#ifndef MULTIPLE_THREADS
    if (SigioHandler::hasSigioFired()) {
	SigioHandler::checkAndCallTraps ();
    }
#endif

	/* request the meeting */

#ifdef FIX_LATER
	/* we are doing extra work here since agentRequestMeeting adds */
	/* another permit to the set of Restrictions                   */
#endif

    if ((code = agentRequestMeeting (delay, id, handle)) != AGENT_OK) {
	return (code);
    }

	/* get the meeting instance */

    MEETING *meeting = meetings -> lookup_noLock (handle);

	/* wait until the meeting is rejected, broken or complete */

    while (1) {

	UINT_8 meetingStatus;

#ifdef FIX_LATER
	    /* we are once again doing extra work since simpleSelect_noLock */
	    /* adds yet another permit to the set of Restrictions           */
#endif

	if ((code = io -> simpleSelect_noLock (*meeting, meetingStatus, delay)) != AGENT_OK) {
	    break;
	}

	if (meetingStatus == MEET_COMPLETE) {
	    code = AGENT_OK;
	    break;
	}

	if ((meetingStatus == MEET_ACCEPTING) || (meetingStatus == MEET_CONNECTING)) {
	    continue;
	}

	if (meetingStatus == MEET_REJECTED) {
	    code = AGENT_MEET_REJECTED;
	    break;
	}

#ifdef FIX_LATER
	    /* we are once again doing extra work since agentAcceptMeeting */
	    /* adds yet another permit to the set of Restrictions          */
#endif

	if (meetingStatus == MEET_REQUESTED) {

	    AgentId acceptedId;

	    if ((code = agentAcceptMeeting (delay, handle, acceptedId)) != AGENT_OK) {
		break;
	    }

	    continue;
	}

	code = AGENT_MEET_BROKEN;
	break;
    }

	/* kill the meeting on error */

    if (code != AGENT_OK) {
	meetings -> killMeeting (handle);
    }

	/* done */

    return (code);
}	


/* agentRequestMeeting

   Purpose: Request a meeting with another agent

     Input: delay = maximum time to wait for server contact
		    (struct timeval)

            id    = identification of the other agent
		    (const AgentId &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent has not been registered
	    AGENT_TIMEOUT      = timeout while trying to contact server
	    AGENT_NO_SERVER    = unable to determine server address
	    AGENT_BAD_RESPONSE = bad response from server
	    AGENT_NONEXISTENT  = target agent does not exist
 
	    On success

	    handle = handle of the new meeting
		     (UINT_32)
*/

int AGENT::agentRequestMeeting (struct timeval delay, const AgentId &recipientId, UINT_32 &handle)
{
    int rc;
    UINT_32 port;
    MESSAGE *response;
    struct timeval stop;
    UINT_32 remoteHandle;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* make a local copy of the AgentId */

    AgentId id (recipientId);

	/* get the IP address of the destination agent */

    TcpipErrors tcpipRc;
    MachineId machine (id.getServerName(), id.getServerIp());

    if ((tcpipRc = tcpip_getIP (machine, stop)) == e_TCPIP_TIMEOUT) {
	return AGENT_TIMEOUT;
    } else if (tcpipRc != e_TCPIP_OK) {
	return AGENT_NO_SERVER;
    }

    id.setServerIp (machine.getIp());

	/* turn the "localhost" IP address into the actual IP address */

    if (id.getServerIp() == location -> getLocalhostIp()) {
	id.setServerIp (location -> localId.getServerIp());
    }

	/* make sure that the agent is not meeting with itself */

    if (id.getServerIp() == location -> localId.getServerIp()) {

	if (!(id.getName().isEmpty()) && !(location -> localId.getName().isEmpty())) {

	    if (id.getName() == location -> localId.getName()) {
		return AGENT_MEET_SELF;
	    }

	} else {

	    if (id.getId() == location -> localId.getId()) {
		return AGENT_MEET_SELF;
	    }
	}
    }
 
	/* see if the destination agent has requested a meeting from us */

    if ((rc = meetings -> acceptMatchingMeeting (id, handle, remoteHandle, port)) == MEET_OK) {

	return (sendAcceptanceMessage (stop, id, handle, remoteHandle, port));

    } else if (rc != MEET_NONEXISTENT) {
	
	return AGENT_UNKNOWN_MEETING;
    }

	/* no meeting request -- so make the new meeting */

    if ((rc = meetings -> makeNewMeeting (id, handle)) != MEET_OK) {
	return AGENT_TOO_MANY_MEETINGS;
    }

	/* request message */

    MESSAGE message (messagesToServer.messages[REQ_MEETING]);
    message.addId (0, &location -> localId);
    message.addString (1, id.getName().value());
    message.addLong (2, id.getId());
    message.addByte (3, ACT_REQUEST);
    message.addLong (4, handle);
    message.addLong (5, UNDEF_MEETING_HANDLE);
    message.addString (6, location -> machine.getName().value());
    message.addIp (7, location -> machine.getIp());
    message.addLong (8, TCPIP_ANY_PORT);

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, rc,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	rc = messageCodeToAgentCode (rc, NULL); 

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        if ((rc = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    rc = messageCodeToAgentCode (rc, connectionPtr); 

	} else if (response == NULL) {

	    rc = AGENT_BAD_RESPONSE;

	} else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag() == RESP_ERROR) {
	        rc = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0));
	    } else {
	        rc = AGENT_SERVER_ERROR;
	    }

        } else {

	    rc = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* if everything is not okay, kill the meeting */

    if (rc != AGENT_OK) {
	(void) meetings -> killMeeting (handle);
    }

    return (rc);
}

/* agentMeetingSendfile

   Purpose: Send a file along a meeting connection

     Input: handle    = handle of the meeting
		        (UINT_32)

	    callbacks = file access callbacks
			(class FileCallbacks &)

	    bytes     = number of bytes to send from the file
		        (UINT_32)

    Output: The procedure returns one of the following codes.

	    AGENT_OK          = success
	    AGENT_NOT_REG     = agent has not been registered
	    AGENT_BAD_MEETING = specified meeting does not exist
	    AGENT_CANT_SEND   = meeting is not connected
	    AGENT_MEET_BROKEN = meeting has been broken
	    AGENT_CANT_READ   = file access error
*/

int AGENT::agentMeetingSendfile (UINT_32 handle, FileCallbacks &callbacks, UINT_32 bytes)
{ 
    int rc;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* try to send the message */

    rc = meetings -> sendFile (handle, callbacks, bytes);

    switch (rc) {

	case MEET_OK:

	    rc = AGENT_OK;
	    break;

	case MEET_NONEXISTENT:

	    rc = AGENT_BAD_MEETING;
	    break;

	case MEET_CANT_SEND:

	    rc = AGENT_CANT_SEND;
	    break;

	case MEET_LOST_CONNECTION:

	    rc = AGENT_MEET_BROKEN;
	    break;

	case MEET_CANT_READ:
	  
	    rc = AGENT_CANT_READ;
	    break;

	default:

	    rc = AGENT_UNKNOWN;
    }

    return (rc);

}


/* agentMeetingSend

   Purpose: Send a message along a meeting

     Input: handle = handle of the meeting
		     (UINT_32)

	    code   = message code
		     (UINT_32)

	    string = message string
		     (char *)

    Output: The procedure returns one of the following codes.

	    AGENT_OK          = success
	    AGENT_NOT_REG     = agent has not been registered
	    AGENT_BAD_MEETING = specified meeting does not exist
	    AGENT_CANT_SEND   = meeting is not connected
	    AGENT_MEET_BROKEN = meeting has been broken
*/

int AGENT::agentMeetingSend (UINT_32 handle, UINT_32 code, char *string)
{
    int rc;

	/* assertions on the parameters */

    assert (string != NULL);

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* try to send the message */

    rc = meetings -> sendMessage (handle, code, string);

    switch (rc) {

	case MEET_OK:

	    rc = AGENT_OK;
	    break;

	case MEET_NONEXISTENT:

	    rc = AGENT_BAD_MEETING;
	    break;

	case MEET_CANT_SEND:

	    rc = AGENT_CANT_SEND;
	    break;

	case MEET_LOST_CONNECTION:

	    rc = AGENT_MEET_BROKEN;
	    break;

	default:

	    rc = AGENT_UNKNOWN;
    }

    return (rc);
}

/* agentCloseMeeting

   Purpose: Close a meeting with another agent

     Input: handle = handle of the meeting
		     (UINT_32)

    Output: The procedure returns one of the following codes.

	    AGENT_OK          = success
	    AGENT_NOT_REG     = agent has not been registered
	    AGENT_BAD_MEETING = specified meeting does not exist 
*/

int AGENT::agentCloseMeeting (UINT_32 handle)
{
    int rc;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* try to kill the meeting */

    rc = meetings -> killMeeting (handle);

    switch (rc) {

	case MEET_OK:

	    rc = AGENT_OK;
	    break;

	case MEET_NONEXISTENT:

	    rc = AGENT_BAD_MEETING;
	    break;

	default:

	    rc = AGENT_UNKNOWN;
    }

    return (rc);
}

/* agentAcceptMeeting

   Purpose: Accept a meeting with another agent

     Input: delay  = maximum time to wait for server contact
		     (struct timeval)

	    handle = handle of the meeting to accept
		     (UINT_32) 

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent has not been registered
	    AGENT_TIMEOUT      = timeout while trying to contact server
	    AGENT_NO_SERVER    = unable to determine server address
	    AGENT_BAD_RESPONSE = bad response from server
	    AGENT_NONEXISTENT  = target agent does not exist
	    AGENT_BAD_MEETING  = specified agent does not exist

	    On success or failure

	    id = full identification of the agent that requested the meeting
		 (AgentId &)
*/

int AGENT::sendAcceptanceMessage (struct timeval stop, const AgentId& id, UINT_32 handle, UINT_32 remoteHandle, UINT_32 port)
{
    int rc;
    MESSAGE *response;

	/* request message */

    MESSAGE message (messagesToServer.messages[REQ_MEETING]);
    message.addId (0, &location -> localId);
    message.addString (1, id.getName().value());
    message.addLong (2, id.getId());
    message.addByte (3, ACT_ACCEPT);
    message.addLong (4, handle);
    message.addLong (5, remoteHandle);
    message.addString (6, location -> machine.getName().value());
    message.addIp (7, location -> machine.getIp());
    message.addLong (8, port);

	/* machine */

    MachineId machine (id.getServerName(), id.getServerIp());

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, rc,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	rc = messageCodeToAgentCode (rc, NULL); 

    } else { 

        AgentConnectionProxy &connection = *connectionPtr;

        if ((rc = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

	    rc = messageCodeToAgentCode (rc, connectionPtr); 

        } else if (response == NULL) {

	    rc = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag() == RESP_ERROR) {
	        rc = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0));
	    } else {
	        rc = AGENT_SERVER_ERROR;
	    }

        } else {

	    rc = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* if everything is not okay, fail the meeting */

    if (rc != AGENT_OK) {
	(void) meetings -> failMeeting (handle);
    }

	/* done */

    return (rc);
}

int AGENT::agentAcceptMeeting (struct timeval delay, UINT_32 handle, AgentId &id)
{
    int rc;
    UINT_32 port;
    struct timeval stop;
    UINT_32 remoteHandle;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* do the actual acceptance */

    if ((rc = meetings -> acceptMeeting (handle, remoteHandle, id, port)) != MEET_OK) {

	switch (rc) {

	    case MEET_NONEXISTENT:

		rc = AGENT_BAD_MEETING;
		break;

	    case MEET_CANT_ACCEPT:

		rc = AGENT_CANT_ACCEPT;
		break;

	    default:

		rc = AGENT_UNKNOWN_MEETING;
	}

	return (rc);
    }

	/* send the acceptance message */

    rc = sendAcceptanceMessage (stop, id, handle, remoteHandle, port);
    return (rc);
}

/* agentRejectMeeting

   Purpose: Reject a meeting with another agent

     Input: delay  = maximum time to wait for server contact
		     (struct timeval)

	    handle = handle of the meeting to reject
		     (UINT_32) 

    Output: The procedure returns one of the following codes.

	    AGENT_OK           = success
	    AGENT_NOT_REG      = agent has not been registered
	    AGENT_TIMEOUT      = timeout while trying to contact server
	    AGENT_NO_SERVER    = unable to determine server address
	    AGENT_BAD_RESPONSE = bad response from server
	    AGENT_NONEXISTENT  = target agent does not exist

	    On success or failure

	    id = full identification of the agent that requested the meeting 
		 (AgentId &)
*/

int AGENT::agentRejectMeeting (struct timeval delay, UINT_32 handle, AgentId &id)
{
    int rc;
    MESSAGE *response;
    struct timeval stop;
    UINT_32 remoteHandle;

	/* check whether the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* add the permit */

    Permit permit;
    permit.setWallLimit (delay);
    RestrictMgr restrictMgr (*restrict, permit, (TimerTrap *) NULL);
    restrict -> getWallLimit (stop);

	/* try to accept the meeting */

    if ((rc = meetings -> rejectMeeting (handle, remoteHandle, id)) != MEET_OK) {

	switch (rc) {

	    case MEET_NONEXISTENT:

		rc = AGENT_BAD_MEETING;
		break;

	    case MEET_CANT_REJECT:

		rc = AGENT_CANT_REJECT;
		break;

	    default:

		rc = AGENT_UNKNOWN_MEETING;
	}

	return (rc);
    }

	/* request message */

    MESSAGE message (messagesToServer.messages[REQ_MEETING]);
    message.addId (0, &location -> localId);
    message.addString (1, id.getName().value());
    message.addLong (2, id.getId());
    message.addByte (3, ACT_REJECT);
    message.addLong (4, handle);
    message.addLong (5, remoteHandle);
    message.addString (6, location -> machine.getName().value());
    message.addIp (7, location -> machine.getIp());
    message.addLong (8, TCPIP_ANY_PORT);

	/* destination machine */

    MachineId machine (id.getServerName(), id.getServerIp());

	/* connect and send the message */

    AgentConnectionProxy *connectionPtr =
	AgentConnectionProxy::makeConnection 
	    (stop, this, machine, *security, rc,
	     AgentConnectionProxy::e_BACKGROUND_OKAY);
    
    if (connectionPtr == NULL) {

	rc = messageCodeToAgentCode (rc, NULL);

    } else {

        AgentConnectionProxy &connection = *connectionPtr;

        if ((rc = connection -> RPC (message, messagesFromServer, response, NULL)) != MESSAGE_OK) {

  	    rc = messageCodeToAgentCode (rc, connectionPtr);

        } else if (response == NULL) {

	    rc = AGENT_BAD_RESPONSE;

        } else if (response -> getFlag() != RESP_OK) {

	    if (response -> getFlag() == RESP_ERROR) {
	        rc = serverCodeToAgentCode ((ServerInterface::ServerErrorcodes) response -> getByte(0)); 
	    } else {
	        rc = AGENT_SERVER_ERROR;
	    }

        } else {

	    rc = AGENT_OK;
        }

	DELETE_OBJECT(connectionPtr);
	DELETE_OBJECT(response);
    }

	/* if everything is not okay, fail the meeting */

    if (rc != AGENT_OK) {
	(void) meetings -> failMeeting (handle);
    }

    return (rc);
}

/* AGENT::agentSelect

   Purpose: Wait for an incoming message, event, meeting request AND/OR
	    an incoming message or file on a connected meeting

     Input: delay        = timeout interval
		           (struct timeval)

	    selectParams = items, handles and fds to watch
			   (AgentSelectParameters &)

    Output: The procedure returns one of the following codes.

	    AGENT_OK	          = success
	    AGENT_BAD_MEETING     = no meeting with the specified handles
	    AGENT_NOT_REG         = the agent has not been registered
	    AGENT_TIMEOUT         = timeout before anything was ready
	    AGENT_LOST_BACKGROUND = lost the background connection
	
	    On success, the procedure fills in selectParams to indicate
	    which items, handles and fds were actually ready.
*/

int AGENT::agentSelect 
	(struct timeval delay, AgentSelectParameters &selectParams)
{
    int rc;         

	/* make sure that the agent is registered */

    if (!(location -> isRegistered())) {
	return AGENT_NOT_REG;
    }

	/* wait for an input event */

    rc = io -> select (delay, selectParams);
    return (rc);
}

/* AGENT::agentSeed

   Purpose: Seed the random pool with additional information 

     Input: seed = the seed information
		   (UINT_32)

    Output: The procedure seeds the random pool.
*/

void AGENT::agentSeed (UINT_32 seed)
{
    randPoolAddBytes ((unsigned char *) &seed, sizeof(UINT_32));
}

/* AGENT::agentRandom
  
   Purpose: Return a psuedo-random number -- the number is cryptograpically
  	    secure if the agent has registered with an agent server or was
	    able to contact the local agent server on startup

     Input: None

    Output: The procedure returns the random number.
*/

UINT_32 AGENT::agentRandom (void)
{
    int i;
    UINT_32 number;
    char *nptr = (char *) &number;

    for (i = 0; i < (int) sizeof(UINT_32); i++) {
	nptr[i] = trueRandByte ();
    }

    return (number);
}
