/*******************************************************************************
 * Copyright (c) 2005, 2008 Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Andy Kaylor, Intel - Initial API and Implementation
 *
 * $Id: Connect2AC.c,v 1.79 2008/12/17 19:36:31 jwest Exp $
 *
 *******************************************************************************/ 

#include "tptp/TPTPTypes.h"
#include "tptp/ACFlags.h"
#include "tptp/TPTPUtils.h"
#include "tptp/TPTPSupportTypes.h"
#include "tptp/TransportSupport.h"
#include "tptp/TPTPBaseTL.h"
#include "tptp/BaseTLLog.h"
#include "ClientCTL.h"
#include "Connect2AC.h"
#include "RACClientSupport.h"
#include "tptp/TPTPConfig.h"

#ifdef _WIN32
// TODO It's a workaround to get compiled on WIN IA32, it needs to be resolved.
	#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
	#include "ws2tcpip.h"
#endif

#define CMD_BUFFER_LENGTH 8192 /* TODO: Use a universal definition */
#define MAX_AGENT_NAME    1024 /* TODO: Use a universal definition */

#define WAITING_PC_TIMEOUT 500
#define WAITING_PC_COUNTER 10

/* some commands will need to send messages to process controller... this gobal
 * will cache the process controller ID once we do a getAgent */
static tptp_int32 processControllerDestID = -1;
typedef struct {
	tl_state_data_t *stateData;
	client_data_connection_block_t *dcb;
	tptp_int32 partnerID;
} client_console_thread_info_t;

SOCKET consoleDataSock;

tptp_int32 forwardXmlCommand( tl_state_data_t* stateData, tptp_string* command )
{
	/* Check to make sure we have a good function pointer to send the message */
	if ( stateData->controlPartner.processMessage == NULL )
	{
		return -1;
	}

	/* Send the command */
	stateData->controlPartner.processMessage( stateData->controlPartner.nexto, strlen(command), command );

	return 0;
}

/*******************************************************************************/ 

tptp_int32 handleOutgoingMessage( tl_state_data_t* stateData, client_connection_block_t* ccb, tptp_string* pCmdBlock )
{
	tptp_int32     ret;
	tptp_int32     sourceID;
	tptp_int32     context;
	char*          interfaceID = NULL;
	char*          cmdName = NULL;
	tptp_list_t*   paramList;

	/* Parse the XML into useful blocks */
	ret = parseCommand( pCmdBlock, &sourceID, &context, &interfaceID, &cmdName, &paramList );
	if ( ret != 0 )
	{
		/* We can't respond (because we don't know where to send it)   */
		/*    so log it as an error                                    */
		TPTP_LOG_ERROR_MSG1( stateData, "Error parsing incoming command: %s", pCmdBlock );
		return ret;
	}
	else
	{
		ret = processCommand( stateData, ccb, pCmdBlock, sourceID, context, interfaceID, cmdName, paramList );

		/* Free the resources allocated by parseCommand */
		if (interfaceID != NULL) tptp_free( interfaceID );
		if (cmdName != NULL) tptp_free( cmdName );
		tptp_list_clear( paramList );
		tptp_free( paramList );

		return ret;
	}

	return 0;
}

/*******************************************************************************/ 

tptp_int32 handleRunningAgents( tl_state_data_t* stateData, 
				client_connection_block_t* ccb, 
				tptp_int32 contextID,
				tptp_list_t* paramList )
{
	tptp_list_t *agentInfoList;
	tptp_int32 ret;
	get_agent_list_context_t*  getAgentListContextData;

	/* Get the context data for this command */
	ret = baseTL_getContextData(stateData, contextID, (void**) &getAgentListContextData);
	if ( (ret != 0) || (getAgentListContextData == NULL) || 
		 (getAgentListContextData->contextDataType != GET_AGENT_LIST_CONTEXT_TYPE) )
	{
		/* This is unexpected and bad.  The code is designed to signal a semaphore
		      when this response comes in, but the mismatch just identifies means
			  that we can't find the semaphore.
		   Did you add a new command call that doesn't use get_agent_context_t? */
		TPTP_LOG_ERROR_MSG( stateData, "Unexpected context data in running agents response" );
		return -1;
	}
	//Get the list of agents from the paramList.
	agentInfoList = tptp_malloc(sizeof(tptp_list_t));
	ret = getStringListParam("agentInfoList", paramList, agentInfoList);

	if ( ret == 0 )
	{
		getAgentListContextData->agentInfoList = agentInfoList;
		getAgentListContextData->status = 0;
	}
	else
	{
		getAgentListContextData->status = -1;
	}


	/* Signal completion */
	tptp_postSemaphore( &getAgentListContextData->completionSemaphore );

	return ret;
	
}

/*******************************************************************************/ 

tptp_int32 handleAgentDetails( tl_state_data_t*           stateData, 
                               client_connection_block_t* ccb, 
                               tptp_int32                 contextID,
                               tptp_list_t*               paramList )
{
	tptp_int32           ret;
	get_type_context_t*  getTypeContextData;
	char*                agentType;
	char*                agentUUID;

	/* Get the context data for this command */
	ret = baseTL_getContextData(stateData, contextID, (void**) &getTypeContextData);
	if ( (ret != 0) || (getTypeContextData == NULL) || 
		 (getTypeContextData->contextDataType != GET_TYPE_CONTEXT_TYPE) )
	{
		/* This is unexpected and bad.  The code is designed to signal a semaphore
		      when this response comes in, but the mismatch just identifies means
			  that we can't find the semaphore.
		   Did you add a new command call that doesn't use get_agent_context_t? */
		TPTP_LOG_ERROR_MSG( stateData, "Unexpected context data in agentDetails response" );
		return -1;
	}

	/* Get the agent type */
	ret = getStringParam( "agentType", paramList, &agentType );
	if ( ret == 0 )
	{
		/* Get the agent UUID */
		ret = getStringParam( "agentUUID", paramList, &agentUUID );
		if ( ret == 0 )
		{
			ra_createRASTRING( &getTypeContextData->agentType, agentType );
			ra_createRASTRING( &getTypeContextData->agentUUID, agentUUID );
			getTypeContextData->status = 0;
		}
		else
		{
			getTypeContextData->status = -1;
		}
	}
	else
	{
		getTypeContextData->status = -1;
	}


	/* Signal completion */
	tptp_postSemaphore( &getTypeContextData->completionSemaphore );
	
	return 0;
}

/*******************************************************************************/ 

tptp_int32 waitForAgentDataFlush( tl_state_data_t* stateData, agent_t* agent )
{
	char                        waitFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><waitForDataFlush iid=\"org.eclipse.tptp.legacy\"></waitForDataFlush></Cmd>";
	char                        waitCommand[8192];
	unsigned int                contextID = baseTL_getNextContextID();
	generic_wait_context_t*     waitContextData;
	tptp_int32                  count = 0;

	/* Initialize the context data for this command */
	waitContextData = (generic_wait_context_t*)tptp_malloc(sizeof(generic_wait_context_t));
	if ( waitContextData == NULL )
	{
		TPTP_LOG_ERROR_MSG( stateData, "Out of memory while trying to send waitForDataFlush command" );
		return TPTP_ERROR_OUT_OF_MEMORY;
	}

	waitContextData->contextDataType = WAIT_FOR_FLUSH_CONTEXT_TYPE;
	tptp_initializeSemaphore( &waitContextData->completionSemaphore );
	waitContextData->status = BASETL_STATUS_PENDING;
	waitContextData->timeout = 10000;
	baseTL_storeContextData( stateData, contextID, (void*)waitContextData );

	sprintf( waitCommand, waitFormat, agent->client->clientID, agent->agentID, contextID );

	/* Send the command */
	forwardXmlCommand( stateData, waitCommand );

	/* Wait for the response */
	tptp_waitSemaphore( &waitContextData->completionSemaphore );

	/* Clean up */
	tptp_deleteSemaphore( &waitContextData->completionSemaphore );
	baseTL_releaseContextData( stateData, contextID );

	return count;
}

/*******************************************************************************/ 

tptp_int32 notifyAgentOfProcessScrub( tl_state_data_t* stateData, agent_t* agent, PID pid )
{
	char                        notifyFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><notifyProcessScrub iid=\"org.eclipse.tptp.legacy\"><pid>%lu</pid></notifyProcessScrub></Cmd>";
	char                        notifyCommand[8192];
	unsigned int                contextID = baseTL_getNextContextID();

	TPTP_LOG_DEBUG_MSG1( stateData, "Sending scrub message for process %lu", pid );

	sprintf( notifyCommand, notifyFormat, -1, agent->agentID, contextID, pid );

	/* Send the command */
	return forwardXmlCommand( stateData, notifyCommand );
}

/*******************************************************************************/ 

tptp_int32 stopAgentDataFlush( tl_state_data_t* stateData, agent_t* agent )
{
	char                        stopFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><stopDataFlush iid=\"org.eclipse.tptp.legacy\"></stopDataFlush></Cmd>";
	char                        stopCommand[8192];
	unsigned int                contextID = baseTL_getNextContextID();

	sprintf( stopCommand, stopFormat, -1, agent->agentID, contextID );

	/* Send the command */
	forwardXmlCommand( stateData, stopCommand );

	return 0;
}

/*******************************************************************************/ 

tptp_int32 getAgentTypeAndUUID( tl_state_data_t* stateData, agent_t* agent )
{
	cctl_state_data_t*   cctlData = (cctl_state_data_t*)stateData->implData;
	char                 getTypeAndUUIDFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><getTypeAndUUID iid=\"org.eclipse.tptp.legacy\"></getTypeAndUUID></Cmd>";
	char                 getTypeAndUUIDCommand[8192];
	unsigned int         contextID = baseTL_getNextContextID();
	unsigned int         agentID;
	get_type_context_t*  getTypeContextData;
	tptp_int32           ret;

	if ( agent->agentID == -1 )
	{
		if ( agent->agentToken == -1 )
		{
			/* We have no way of identifying the agent at this point */
			return -1;
		}
		else
		{
			/* This is VERY BAD behavior.  ONLY the AC is supposed to do this.
			   This will break if the way the AC creates tokens changes, and this
			   circumvents the AC's reference counting.

			   However, since this is meant to be a throw-away component and is
			   kind of a logical extension of the AC, I'm doing it anyway.

			   DO NOT EVEN CONSIDER COPYING THIS CODE!!! */
			agentID = (agent->agentToken & (~0x40000000));
			agent->agentID = agentID; 
		}
	}
	else
	{
		agentID = agent->agentID;
	}

	/* Initialize the context data for this command */
	getTypeContextData = (get_type_context_t*)tptp_malloc( sizeof(get_type_context_t) );
	if ( getTypeContextData == NULL )
	{
		/* TODO: Log an error */
		return -1;
	}
	getTypeContextData->contextDataType = GET_TYPE_CONTEXT_TYPE;
	tptp_initializeSemaphore( &getTypeContextData->completionSemaphore );
	getTypeContextData->status = BASETL_STATUS_PENDING;
	baseTL_storeContextData( stateData, contextID, (void*)getTypeContextData );

	/* Request exclusive access to this agent */
	sprintf( getTypeAndUUIDCommand, getTypeAndUUIDFormat, cctlData->selfConnectionID, agentID, contextID );

	/* Send the command */
	forwardXmlCommand( stateData, getTypeAndUUIDCommand );

	/* Start a thread to trigger our semaphore if we get no other response */
	getTypeContextData->timeout = 100;
	baseTL_startTimeoutThread( (generic_wait_context_t*)getTypeContextData );

	/* Wait for the response */
	tptp_waitSemaphore( &getTypeContextData->completionSemaphore );
	if ( getTypeContextData->status == BASETL_STATUS_PENDING )
	{
		/* This probably means our wait failed */
		/* TODO: Log an error? */
		getTypeContextData->status = -1;
	}
	else if ( getTypeContextData->status == 0 )
	{
		/* Success! */
		/* Copy the type and UUID */
		ra_copyRASTRING( &agent->agentType, &getTypeContextData->agentType );
		ra_copyRASTRING( &agent->agentUUID, &getTypeContextData->agentUUID );
	}

	ret = getTypeContextData->status;


	/* If we didn't time out, the timeout thread will clean up when it triggers */
	if ( getTypeContextData->status == BASETL_STATUS_TIMEOUT )
	{
		/* Clean up */
		tptp_deleteSemaphore( &getTypeContextData->completionSemaphore );
		baseTL_releaseContextData( stateData, contextID );
	}

	return ret;
}

BOOL queryAgentList(client_connection_block_t* ccb, PID pid, tptp_list_t* agentInfoList )
{
	char          queryAgentFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><queryRunningAgents iid=\"org.eclipse.tptp.agentManager\"><processID>%lu</processID></queryRunningAgents></Cmd>";
	char          queryAgentCommand[8192];
	unsigned int  contextID = baseTL_getNextContextID();
	get_agent_list_context_t*  getAgentListContextData;
	tptp_int32 ret;
	tptp_node_t*    node;

	tptp_list_init(agentInfoList);
	/* Initialize the context data for this command */
	getAgentListContextData = (get_agent_list_context_t*)tptp_malloc( sizeof(get_agent_list_context_t) );
	if ( getAgentListContextData == NULL )
	{
		/* TODO: Log an error */
		return -1;
	}
	getAgentListContextData->contextDataType = GET_AGENT_LIST_CONTEXT_TYPE;
	tptp_initializeSemaphore( &getAgentListContextData->completionSemaphore );
	getAgentListContextData->status = BASETL_STATUS_PENDING;
	baseTL_storeContextData( ccb->stateData, contextID, (void*)getAgentListContextData );

	/* Look for agents with the specified process ID */
	sprintf( queryAgentCommand, queryAgentFormat, ccb->clientID, AGENT_MANAGER, contextID, pid );

	forwardXmlCommand( ccb->stateData, queryAgentCommand );

	/* Start a thread to trigger our semaphore if we get no other response */
	getAgentListContextData->timeout = 100;
	baseTL_startTimeoutThread( (generic_wait_context_t*)getAgentListContextData );

	/* Wait for the response */
	tptp_waitSemaphore( &getAgentListContextData->completionSemaphore );
	if ( getAgentListContextData->status == BASETL_STATUS_PENDING )
	{
		/* This probably means our wait failed */
		/* TODO: Log an error? */
		getAgentListContextData->status = -1;
	}
	else if ( getAgentListContextData->status == 0 )
	{
		/* Success! */
		if (getAgentListContextData->agentInfoList->count > 0) {
			for (node = getAgentListContextData->agentInfoList->head; node != 0; node = node->next) {
				tptp_list_add(agentInfoList, (void *) node);
			}
		}
	}

	ret = getAgentListContextData->status;


	/* If we didn't time out, the timeout thread will clean up when it triggers */
	if ( getAgentListContextData->status == BASETL_STATUS_TIMEOUT )
	{
		/* Clean up */
		tptp_deleteSemaphore( &getAgentListContextData->completionSemaphore );
		baseTL_releaseContextData( ccb->stateData, contextID );
	}

	return TRUE;
}

/*******************************************************************************/ 

tptp_int32 handleAgentReference( tl_state_data_t* stateData, 
				client_connection_block_t* ccb, 
				tptp_int32 contextID,
				tptp_list_t* paramList )
{
	tptp_int32 ret;
	get_agent_context_t*  getAgentContextData;

	/* Get the context data for this command */
	ret = baseTL_getContextData(stateData, contextID, (void**) &getAgentContextData);
	if ( (ret != 0) || (getAgentContextData == NULL) || 
		 (getAgentContextData->contextDataType != GET_AGENT_CONTEXT_TYPE) )
	{
		/* This is unexpected and bad.  The code is designed to signal a semaphore
		      when this response comes in, but the mismatch just identifies means
			  that we can't find the semaphore.
		   Did you add a new command call that doesn't use get_agent_context_t? */
		TPTP_LOG_ERROR_MSG( stateData, "Unexpected context data in agentReference response" );
		getAgentContextData->status = -1;
		return -1;
	}

	/* Get the agent ID */
	ret = getIntegerParam( "agentID", paramList, &getAgentContextData->agentID );
	getAgentContextData->status = 0;

	/* Signal completion */
	tptp_postSemaphore( &getAgentContextData->completionSemaphore );
	
	return 0;
}

/*******************************************************************************/ 

tptp_int32 handlePropertyList( tl_state_data_t*           stateData, 
				               client_connection_block_t* ccb, 
                               tptp_int32                 contextID,
                               tptp_list_t*               paramList )
{
	tptp_int32                    ret;
	get_property_list_context_t*  gplContextData;
	tptp_list_t                   propertyList;
	ra_message_t*                 replyMessage;
	ra_command_t*                 replyCommand;

	/* Get the context data for this command */
	ret = baseTL_getContextData(stateData, contextID, (void**)&gplContextData);
	if ( (ret != 0) || (gplContextData == NULL) )
	{
		TPTP_LOG_ERROR_MSG( stateData, "Missing context data in propertyList response" );
		return -1;
	}

	if ( gplContextData->contextDataType != GET_PROPLIST_CONTEXT_TYPE )
	{
		/* Normally, we free the context data we find, but since this didn't match
		   our expectation, we can't be sure we should free it.  Leaks are better than crashes */
		TPTP_LOG_ERROR_MSG( stateData, "Unexpected context data in propertyList response" );
		return -1;
	}

	/* Get the properties */
	ret = getStringListParam( "propertyListEntry", paramList, &propertyList );


	replyMessage = ra_createMessage(RA_CONTROL_MESSAGE, 0);
	replyCommand = ra_addCommandToMessage(replyMessage, NULL);
	replyCommand->tag = RA_PROPERTY_LIST;
	replyCommand->info.property_list.context = gplContextData->clientContext;

	if ( (ret != 0) || (propertyList.count == 0) )
	{
		replyCommand->info.property_list.entries.length = 0;
	}
	else
	{
		tptp_node_t* node;
		int          i = 0;

		replyCommand->info.property_list.entries.data = (void**)tptp_malloc(sizeof(ra_agentConfigEntry_t*) * propertyList.count);
		replyCommand->info.property_list.entries.length = propertyList.count;

		for ( node = propertyList.head; node != NULL; node = node->next )
		{
			char* name;
			char* type;
			char* value;

			ret = parsePropertyListEntry( (char*)node->data, &name, &type, &value );
			if ( ret == 0 )
			{
				((ra_agentConfigEntry_t**)replyCommand->info.property_list.entries.data)[i] = (ra_agentConfigEntry_t*)tptp_malloc(sizeof(ra_agentConfigEntry_t));
				BZERO(((ra_agentConfigEntry_t**)replyCommand->info.property_list.entries.data)[i], sizeof(ra_agentConfigEntry_t));
				ra_createRASTRING(&((ra_agentConfigEntry_t**)replyCommand->info.property_list.entries.data)[i]->name, name);
				ra_createRASTRING(&((ra_agentConfigEntry_t**)replyCommand->info.property_list.entries.data)[i]->type, type);
				ra_createRASTRING(&((ra_agentConfigEntry_t**)replyCommand->info.property_list.entries.data)[i]->value, value);
				i++;

				tptp_free(name);
				tptp_free(type);
				tptp_free(value);
			}


		}
	}

	ra_forwardMessage(replyMessage, ccb);
	ra_destroyMessage(replyMessage, TRUE);

	/* Free our context data */
	baseTL_releaseContextData( stateData, contextID );

	return 0;
}

/*******************************************************************************/ 

tptp_int32 handleDataPathEstablished( tl_state_data_t* stateData, 
				client_connection_block_t* ccb, 
				tptp_int32 contextID,
				tptp_list_t* paramList )
{
	tptp_int32 ret;
	establish_data_path_context_t*  establishDataPathContextData;

	/* Get the context data for this command */
	ret = baseTL_getContextData(stateData, contextID, (void**) &establishDataPathContextData);
	if ( (ret != 0) || (establishDataPathContextData == NULL) || 
		 (establishDataPathContextData->contextDataType != ESTABLISH_DATA_PATH_CONTEXT_TYPE) )
	{
		/* This is unexpected and bad.  The code is designed to signal a semaphore
		      when this response comes in, but the mismatch just identifies means
			  that we can't find the semaphore.
		   Did you add a new command call that doesn't use establish_data_path_context_t? */
		TPTP_LOG_ERROR_MSG( stateData, "Unexpected context data in establishedDataPath response" );
		return -1;
	}

	/* Signal completion */
	establishDataPathContextData->status = 0;
	tptp_postSemaphore( &establishDataPathContextData->completionSemaphore );
	
	return 0;
}

/*******************************************************************************/ 

tptp_int32 handleProcessStarted( tl_state_data_t* stateData, 
				client_connection_block_t* ccb, 
				tptp_int32 contextID,
				tptp_list_t* paramList )
{
	tptp_int32 ret;
	start_process_context_t*  startProcessContextData;

	/* Get the context data for this command */
	ret = baseTL_getContextData(stateData, contextID, (void**) &startProcessContextData);
	if ( (ret != 0) || (startProcessContextData == NULL) || 
		 (startProcessContextData->contextDataType != START_PROCESS_CONTEXT_TYPE) )
	{
		/* This is unexpected and bad.  The code is designed to signal a semaphore
		      when this response comes in, but the mismatch just identifies means
			  that we can't find the semaphore.
		   Did you add a new command call that doesn't use get_agent_context_t? */
		TPTP_LOG_ERROR_MSG( stateData, "Unexpected context data in startProcess response" );
		return -1;
	}

	/* Get the process ID */
	ret = getPIDParam( "processID", paramList, &startProcessContextData->processID );
	if ( ret != 0 )
	{
		/* TODO: Log an error */
		startProcessContextData->status = -1;
		tptp_postSemaphore( &startProcessContextData->completionSemaphore );
		return -1;
	}
	/* Get the process UUID */
	ret = getStringParam( "processUUID", paramList, &startProcessContextData->processUUID );
	if ( ret != 0 )
	{
		/* TODO: Log an error */
		startProcessContextData->status = -1;
		tptp_postSemaphore( &startProcessContextData->completionSemaphore );
		return -1;
	}

	/* Get the Environment */
	startProcessContextData->finalEnvCount = 0;
	startProcessContextData->finalEnv = 0;
	ret = getIntegerParam( "envVarCount", paramList, &(startProcessContextData->finalEnvCount) );
	ret = getStringParam( "envVarList", paramList, &(startProcessContextData->finalEnv) );

	/* Set the status to success */
	startProcessContextData->status = 0;

	/* Signal completion */
	tptp_postSemaphore( &startProcessContextData->completionSemaphore );
	
	return 0;
}

tptp_int32 handleProcessExitEvent( tl_state_data_t* stateData, 
				client_connection_block_t* ccb, 
				tptp_int32 contextID,
				tptp_list_t* paramList )
{
	tptp_int32 ret;
	PID pid;
	tptp_int32 exitCode;
	process_t *process;
	cctl_state_data_t*  cctlData;

	cctlData = (cctl_state_data_t*)ccb->stateData->implData;

	/* Get the process ID */
	ret = getIntegerParam( "processID", paramList, &pid );
	ret = getIntegerParam( "exitCode", paramList, &exitCode );
	if ( ret != 0 )
	{
	/* This parameter isn't critical, so we can use a default value */
		exitCode = 0;
	}	
	ra_mutexEnter( &cctlData->processList->lock );
	process = ra_processFind(cctlData->processList, pid);
	if (process ) {
		scrubProcess(cctlData->processList, process, stateData);
	} else {
		//TODO need an error message for this!
	}
	ra_mutexExit( &cctlData->processList->lock );

	return 0;
}

/*******************************************************************************/ 

BOOL killProcess( client_connection_block_t* ccb, PID pid)
{
	char          killProcessFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><stopProcess iid=\"org.eclipse.tptp.processController\"><processID>%lu</processID></stopProcess></Cmd>";
	char          killProcessCommand[8192];
	unsigned int  contextID = baseTL_getNextContextID();
	tptp_int32    status;

	if (processControllerDestID == -1) {
		status = getAgent(ccb->stateData, ccb, "ProcessController", &processControllerDestID);
	}

	sprintf( killProcessCommand, killProcessFormat, ccb->clientID, processControllerDestID, contextID, pid);

	/* there is no response to this command being sent... other than an error... which still is not passed  */
	/* back to the client  											*/
	forwardXmlCommand( ccb->stateData, killProcessCommand);

	return TRUE;
}

/*******************************************************************************/ 

tptp_int32 establishDataPath(tl_state_data_t* stateData, client_connection_block_t* ccb, agent_t* agent, tptp_int32 dataConnectionID, tptp_int32 direction )
{
    tptp_int32                     status;
	unsigned int                   contextID = baseTL_getNextContextID();
	establish_data_path_context_t* establishDataPathContextData;

	char          establishDataPathFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><establishDataPath iid=\"org.eclipse.tptp.dataProvider\"><dataConnectionID>%d</dataConnectionID><flags>%d</flags></establishDataPath></Cmd>";
	char          establishDataPathCommand[8192];

	/* Initialize the context data for this command */
	establishDataPathContextData = (establish_data_path_context_t*)tptp_malloc( sizeof(establish_data_path_context_t) );
	if ( establishDataPathContextData == NULL )
	{
		/* TODO: Log an error */
		return -1;
	}
	establishDataPathContextData->contextDataType = ESTABLISH_DATA_PATH_CONTEXT_TYPE;
	tptp_initializeSemaphore( &establishDataPathContextData->completionSemaphore );
	establishDataPathContextData->status = BASETL_STATUS_PENDING;
	baseTL_storeContextData( ccb->stateData, contextID, (void*)establishDataPathContextData );

	sprintf(establishDataPathCommand, establishDataPathFormat,  ccb->clientID, agent->agentID, contextID, dataConnectionID, direction);	

	/* Send the command */
	forwardXmlCommand( ccb->stateData, establishDataPathCommand );

	/* Wait for the response */
	tptp_waitSemaphore( &establishDataPathContextData->completionSemaphore );
	if ( establishDataPathContextData->status == BASETL_STATUS_PENDING )
	{
		/* This probably means our wait failed */
		/* TODO: Log an error? */
		establishDataPathContextData->status = -1;
	}
	else if ( establishDataPathContextData->status == 0 )
	{
		/* Success! */
	}

	status = establishDataPathContextData->status;

	/* Clean up */
	tptp_deleteSemaphore( &establishDataPathContextData->completionSemaphore );
	baseTL_releaseContextData( stateData, contextID );

	return status;
}

/*******************************************************************************/ 

tptp_int32 releaseDataPath(tl_state_data_t* stateData, client_connection_block_t* ccb, agent_t* agent)
{

	unsigned int         contextID = baseTL_getNextContextID();

	char          releaseDataPathFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><releaseDataPath iid=\"org.eclipse.tptp.dataProvider\"><dataConnectionID>%d</dataConnectionID></releaseDataPath></Cmd>";
	char          releaseDataPathCommand[8192];

	sprintf(releaseDataPathCommand, releaseDataPathFormat,  ccb->clientID, agent->agentID, contextID, -1);

	/* Send the command */
	forwardXmlCommand( ccb->stateData, releaseDataPathCommand );

	/* There is no response to this command! */

	return 0;
}


/*******************************************************************************/ 

tptp_int32 getAgent(  tl_state_data_t* stateData, client_connection_block_t* ccb, char *agentName, tptp_int32* agentID )
{
    tptp_int32           status;
	char                 getAgentFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><getAgent iid=\"org.eclipse.tptp.agentManager\"><agentName>%s</agentName><flags>%lu</flags></getAgent></Cmd>";
	char                 getAgentCommand[8192];
	unsigned int         contextID = baseTL_getNextContextID();
	get_agent_context_t* getAgentContextData;

	/* Initialize the context data for this command */
	getAgentContextData = (get_agent_context_t*)tptp_malloc( sizeof(get_agent_context_t) );
	if ( getAgentContextData == NULL )
	{
		/* TODO: Log an error */
		return -1;
	}
	getAgentContextData->contextDataType = GET_AGENT_CONTEXT_TYPE;
	tptp_initializeSemaphore( &getAgentContextData->completionSemaphore );
	getAgentContextData->agentID = -1;
	getAgentContextData->status = BASETL_STATUS_PENDING;
	baseTL_storeContextData( ccb->stateData, contextID, (void*)getAgentContextData );

	sprintf( getAgentCommand, getAgentFormat, ccb->clientID, AGENT_MANAGER, contextID, agentName, TPTP_CONTROLLER_ACCESS);

	/* Send the command */
	forwardXmlCommand( stateData, getAgentCommand );

	/* Wait for the response */
	tptp_waitSemaphore( &getAgentContextData->completionSemaphore );
	if ( getAgentContextData->status == BASETL_STATUS_PENDING )
	{
		/* This probably means our wait failed */
		/* TODO: Log an error? */
		getAgentContextData->status = -1;
	}
	else if ( getAgentContextData->status == 0 )
	{
		/* Success! */
		*agentID = getAgentContextData->agentID;
	}

	status = getAgentContextData->status;

	/* Clean up */
	tptp_deleteSemaphore( &getAgentContextData->completionSemaphore );
	baseTL_releaseContextData( stateData, contextID );

	return status;
}

/*******************************************************************************/ 

tptp_int32 getAgentByToken( tl_state_data_t* stateData, client_connection_block_t* ccb, agent_t* agent )
{
    tptp_int32           status;
	char                 getAgentByTokenIdFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><getAgentByToken iid=\"org.eclipse.tptp.agentManager\"><token>%lu</token><flags>%lu</flags></getAgentByToken></Cmd>";
	char                 getAgentByTokenIdCommand[8192];
	unsigned int         contextID = baseTL_getNextContextID();
	get_agent_context_t* getAgentContextData;

	/* Initialize the context data for this command */
	getAgentContextData = (get_agent_context_t*)tptp_malloc( sizeof(get_agent_context_t) );
	if ( getAgentContextData == NULL )
	{
		/* TODO: Log an error */
		return -1;
	}
	getAgentContextData->contextDataType = GET_AGENT_CONTEXT_TYPE;
	tptp_initializeSemaphore( &getAgentContextData->completionSemaphore );
	getAgentContextData->agentID = -1;
	getAgentContextData->status = BASETL_STATUS_PENDING;
	baseTL_storeContextData( ccb->stateData, contextID, (void*)getAgentContextData );

	/* Request exclusive access to this agent */
	sprintf( getAgentByTokenIdCommand, getAgentByTokenIdFormat, ccb->clientID, AGENT_MANAGER, contextID, agent->agentToken, TPTP_CONTROLLER_ACCESS | TPTP_LOCK_AGENT );

	/* Send the command */
	forwardXmlCommand( ccb->stateData, getAgentByTokenIdCommand );

	/* Wait for the response */
	tptp_waitSemaphore( &getAgentContextData->completionSemaphore );
	if ( getAgentContextData->status == BASETL_STATUS_PENDING )
	{
		/* This probably means our wait failed */
		/* TODO: Log an error? */
		getAgentContextData->status = -1;
	}
	else if ( getAgentContextData->status == 0 )
	{
		/* Success! */
		agent->agentID = getAgentContextData->agentID;
	}

	status = getAgentContextData->status;

	/* Clean up */
	tptp_deleteSemaphore( &getAgentContextData->completionSemaphore );
	baseTL_releaseContextData( stateData, contextID );

	return status;
}

THREAD_USER_FUNC_RET_TYPE processClientConsole(LPVOID args) 
{
	int    rc = 1;

	SOCKET clientSock ;

	unsigned int  bytesRead;
	unsigned char buffer[TPTP_DEFAULT_BUFFER_LENGTH+1];
	unsigned int  bufferLength = TPTP_DEFAULT_BUFFER_LENGTH;
	int           offset = 0;
	removeConnectionEntry_ptr_t removeConnection = NULL;

	/* set up environmental info for this incoming message */
	client_console_thread_info_t* cci = (client_console_thread_info_t*)args;
	client_data_connection_block_t* dcb = cci->dcb;

	clientSock = dcb->sock ;

	while ( (rc = readFromSocket(clientSock, buffer+offset, bufferLength-offset, &bytesRead)) > 0)
	{
		tl_data_connection_t* dataConnection = (tl_data_connection_t *) tableGet(cci->stateData->dataConnectionTable, cci->partnerID);
		dataConnection->dataPartner.sendData(
			dataConnection->dataPartner.nexto,
			dataConnection->dataPartner.connectionID,
		       	bytesRead, buffer);
	}

	closeThisSocket(clientSock);

	/* Remove this connection from our table */
	//kevin need to figure this out.
	//baseTL_removeDataConnectionEntry( ccb->stateData, ccb->clientID );

	return ( 0 ) ;
}

/* These 2 functions assume a str of the format
 * CLASSPATH=blah;blah;blah
 * Env name will be CLASSPATH and
 * Value will be blah;blah;blah
 * These functions allocate memory that need to be free'ed */

char *findEnvName(char *str)
{
	char *name;
	char *p;

	if (str == NULL) {
		return NULL;
	}
	p = strchr(str, '='); /* a var name ends with equal */
	if (p == NULL) {
		//str is not in the form we expect... exit fast.
		return NULL;
	}
	name = tptp_malloc(p-str+1); 
	strncpy(name, str, p-str); /* copy the name from str and terminate with null */
	name[p-str] = '\0';

	return name;
}

char *findEnvValue(char *str)
{
	char *value;
	char *p;

	if (str == NULL) {
		return NULL;
	}
	value = tptp_malloc(strlen(str) +1);
	p = strchr(str, '='); /* look for the equal and go 1 beyond */

	if (p == NULL) {
		//str is not in the form we expect... exit fast.
		return NULL;
	}
	strcpy(value, p+1);

	return value;
}

/*******************************************************************************/ 

PID startProcess( client_connection_block_t* ccb, 
	ra_string_t *exe,
	ra_string_t *args,
	ra_string_t *location,
	ra_array_t *environment,
	unsigned long remoteConsoleIP,
	unsigned short remoteConsolePort,
	ra_array_t *finalEnvironment,
	SOCKET *consoleSock,
	ra_string_t *uuid) 
{
	char          startProcessFormat[] = "<Cmd src=\"%d\" dest=\"%d\" ctxt=\"%d\"><startProcess iid=\"org.eclipse.tptp.processController\"><launchInfo>%s</launchInfo><consoleConnectID>%d</consoleConnectID></startProcess></Cmd>";
	char          *startProcessCommand;
	char          *launchStr;
	char          *parameters;
	char 	      *tempBuffer;
	char	      *variable;
	char 	      *variableList;
	tptp_int32    variableListSize = 8192;
	char 	      *name;
	char 	      *value;

	unsigned int  contextID = baseTL_getNextContextID();
	tptp_int32    status;
	PID pid = -1;
	start_process_context_t*  startProcessContextData;
	tptp_int32 flags = TPTP_DATA_PATH_TWOWAY;
	tptp_int32 dataConnectionID;
	SOCKET     dataSock;
	
	struct sockaddr_storage * addr;

	client_data_connection_block_t *dcb;
	tptp_int32 rc;
	TID                 threadId;
	HANDLE              threadHandle;
	client_console_thread_info_t* cci;
	unsigned int i;
	cctl_state_data_t*  cctlData = (cctl_state_data_t*)ccb->stateData->implData;
	int cnt;

	cnt = 0;
	while (processControllerDestID < 0 && cnt < WAITING_PC_COUNTER) {
		if (cnt > 0) tptpSleep(WAITING_PC_TIMEOUT);
		
		status = getAgent(ccb->stateData, ccb, "ProcessController", &processControllerDestID);
		
		cnt++;
	}

	if (processControllerDestID < 0) {
		TPTP_LOG_ERROR_MSG(ccb->stateData, "Process Controller not found");
		return -1;
	}

	getClientIPAddr(ccb, &addr);

	dataSock = openClientDataSocketAddr(addr, remoteConsolePort);

	tptp_free(addr);

	if (dataSock != -1) {

		dcb = (client_data_connection_block_t*)tptp_malloc( sizeof(client_data_connection_block_t) );
		if ( dcb == NULL ) {
			return FALSE;
		}
		dcb->sock    = dataSock;
		dcb->ccb     = ccb;
		dcb->context = 0;
		dcb->pid     = 0;
		dcb->agentName.data = 0;
		dcb->agentName.length = 0;

		rc = ccb->stateData->ac.addDataConnection( ccb->stateData->ac.cmo, ccb->stateData->transportID, flags, &dataConnectionID );
		baseTL_addDataConnectionEntry( ccb->stateData, dataConnectionID, (void*)dcb );
		cci = (client_console_thread_info_t*)tptp_malloc( sizeof(client_console_thread_info_t) );
		cci->dcb = dcb;
		cci->stateData = ccb->stateData;
		cci->partnerID = dataConnectionID;
	}

	// this loop is a little complicated.
	// First break apart the individual env variables and bring them together into a variableList.
	// We could do some optimization of the strcpy... TBD
	for(i=0; i<environment->length; i++) {
		name  = findEnvName(((ra_string_t*)(environment->data[i]))->data);
		value = findEnvValue(((ra_string_t*)(environment->data[i]))->data);
		//if we cannot process the env names and values then we should
		//not try to continue.
		if (name == NULL) {
			if (value != NULL) {
				tptp_free(value);
			}
			break;
		}
		if (value == NULL) {
			tptp_free(name);
			break;
		}
		if (i == 0) {
			if ((strlen(name) + strlen(value) + 100) > variableListSize)
			{
				variableListSize = (strlen(name) + strlen(value) + 100) * 2;
			}
			variableList = (char *) tptp_malloc(variableListSize);
			tempBuffer = (char *) tptp_malloc(variableListSize);
			sprintf(variableList, "<Variable name=\"%s\" position=\"%s\" value=\"%s\" />", name, "append", value);
		} else {
			variable = (char *) tptp_malloc(strlen(name) + strlen(value) + 100);
			sprintf(variable, "<Variable name=\"%s\" position=\"%s\" value=\"%s\" />", name, "append", value);
			if ((tptp_int32) (strlen(variableList) + strlen(variable)) > variableListSize)
			{
				variableListSize= variableListSize * 2;
				variableList = (char *) tptp_realloc(variableList, variableListSize);
				tempBuffer = (char *) tptp_realloc(tempBuffer, variableListSize);
			}
			sprintf(tempBuffer, "%s%s", variable, variableList);
			strcpy(variableList, tempBuffer);
			tptp_free(variable);
		}
		tptp_free(name);
		tptp_free(value);
	}

	parameters = (char *) tptp_malloc(args->length + 100);
	sprintf(parameters, "<Parameter position=\"append\">%s</Parameter>", args->data);
	
	if (environment->length > 0) { // yes we are being passed an environment
		launchStr = (char *) tptp_malloc(strlen(exe->data) + strlen(location->data) + strlen(parameters) + strlen(variableList) + 100);
		sprintf(launchStr, "<Application executable=\"%s\" location=\"%s\"> %s %s </Application>", exe->data, location->data, parameters, variableList);
		tptp_free(tempBuffer);
		tptp_free(variableList);
	} else { // no env
		launchStr = (char *) tptp_malloc(strlen(exe->data) + strlen(location->data) + strlen(parameters)+ 100);
		sprintf(launchStr, "<Application executable=\"%s\" location=\"%s\"> %s </Application>", exe->data, location->data, parameters);
	}

	
	//allow for variable size if ID's 
	startProcessCommand = (char *) tptp_malloc(strlen(startProcessFormat) + strlen(launchStr) + 32);
	sprintf( startProcessCommand, startProcessFormat, ccb->clientID, processControllerDestID, contextID, launchStr, dataConnectionID);
	tptp_free(launchStr);
	tptp_free(parameters);

	startProcessContextData = (start_process_context_t*)tptp_malloc( sizeof(start_process_context_t) );
	if ( startProcessContextData == NULL )
	{
		/* TODO: Log an error */
		return -1;
	}
	startProcessContextData->contextDataType = START_PROCESS_CONTEXT_TYPE;
	tptp_initializeSemaphore( &startProcessContextData->completionSemaphore );
	startProcessContextData->processID = -1;
	startProcessContextData->status = BASETL_STATUS_PENDING;
	baseTL_storeContextData( ccb->stateData, contextID, (void*)startProcessContextData );

	forwardXmlCommand( ccb->stateData, startProcessCommand);
	tptp_free(startProcessCommand);

	/* Wait for the response */
	ra_mutexExit( &cctlData->processList->lock );
	tptp_waitSemaphore( &startProcessContextData->completionSemaphore );
	ra_mutexEnter( &cctlData->processList->lock );
	if ( startProcessContextData->status == BASETL_STATUS_PENDING )
	{
		/* This probably means our wait failed */
		/* TODO: Log an error? */
		startProcessContextData->status = -1;
		pid = -1;
	}
	else if ( startProcessContextData->status == 0 )
	{
		//don't start processing client input until we have an agent
		//running.
		if (dataSock != -1) {
			rc = tptpStartThread(processClientConsole, 
				(LPVOID)cci, &threadId, &threadHandle) ;
			CLOSE_THREAD_HANDLE(threadHandle);
		}
		/* Success! */
		pid = startProcessContextData->processID;
		ra_createRASTRING( uuid, startProcessContextData->processUUID );

		/* Get the Environment */
		finalEnvironment->length = 0;
		finalEnvironment->data = 0;
		if (startProcessContextData->finalEnvCount > 0)
		{
			char* lpszVariable = 0;
			int dataStrLen = 0;
			int finalEnvStrLength = 0;
			char* finalEnvStr = 0;
			char* traversePtr = 0;
			int EnvVarLength = 0;
			char* EnvString = 0;
			ra_string_t* raEnvString = 0;
			int i=0;
			int len;

			// Get the EnvironCount and assign environment ra_string_t of that size			
			finalEnvironment->data=(void**)tptp_malloc(sizeof(ra_string_t*)*startProcessContextData->finalEnvCount);

			// Decode the Environment to get the actual environ string
			// We are encoding/decoding the envinroment due to it's format which contains null terminators
			// after each environment string
			dataStrLen = strlen(startProcessContextData->finalEnv);

			/* Calculate the size of buffer required */
			len = tptp_decodeBase64( startProcessContextData->finalEnv, dataStrLen, NULL, 0 );
			finalEnvStr = (char*)tptp_malloc(sizeof(char) * len);
			tptp_decodeBase64( startProcessContextData->finalEnv, dataStrLen, finalEnvStr, len );
			finalEnvStrLength = len;
			
			// Traverse and build environment strings
			for (lpszVariable = finalEnvStr; *lpszVariable; lpszVariable++) 
			{
				traversePtr = lpszVariable;
				EnvVarLength = 0;
				while (*lpszVariable)
				{
					lpszVariable++;
					EnvVarLength++;
				}

				// Build the Environment string and copy
				((ra_string_t**)finalEnvironment->data)[i] = (ra_string_t*)tptp_malloc(sizeof(ra_string_t));
				((ra_string_t*)((ra_string_t**)finalEnvironment->data)[i])->length = EnvVarLength+1;
				if (EnvVarLength > 0)
				{
					((ra_string_t*)((ra_string_t**)finalEnvironment->data)[i])->data = (char*)tptp_malloc(EnvVarLength+1);
					strcpy(((ra_string_t*)((ra_string_t**)finalEnvironment->data)[i])->data, traversePtr);
				}
				else
				{
					((ra_string_t*)((ra_string_t**)finalEnvironment->data)[i])->data = 0;
				}
				i++;
				if(*lpszVariable == '\0' && *(lpszVariable+1) == '\0')
				{
					break;
				}
			}
			finalEnvironment->length = i;

			if (finalEnvStr) tptp_free(finalEnvStr);			
		}

		tptp_free(startProcessContextData->processUUID);
		tptp_free(startProcessContextData->finalEnv);
	} else {
		pid = -1;
	}

	/* Clean up */
	tptp_deleteSemaphore( &startProcessContextData->completionSemaphore );
	baseTL_releaseContextData( ccb->stateData, contextID );

	return pid;
}

tptp_int32 findAgentName(tptp_string* agentName, tptp_string* legacyName)
{
	if (strlen(agentName) < strlen(PLUGIN_LEGACY_PREFIX)) {
		return 0;
	} else {
		if (strstr(agentName, PLUGIN_LEGACY_PREFIX)) {
			agentName+=strlen(PLUGIN_LEGACY_PREFIX);
			strcpy(legacyName, agentName);
			return 1;
		}
	}
	return 0;
}
/*******************************************************************************/ 

tptp_int32 handleAgentRegistered( tl_state_data_t*           stateData, 
                                  tptp_string*               agentName, 
                                  tptp_uint32                token, 
                                  PID                        processId )
{
	cctl_state_data_t*  cctlData = (cctl_state_data_t*)stateData->implData;
	agent_t *           agent;
	process_t *         process;
	ra_string_t         agentNameRASTR;
	char                legacyAgentName[8192];         
	int                 ret;


	//ret = sscanf( agentName, "org.eclipse.tptp.legacy.%s", legacyAgentName );
	ret = findAgentName(agentName, legacyAgentName);
	if ( (ret == 0) || (ret == EOF) )
	{
		/* If the string wasn't in this format, we aren't interested in this agent */
		return 0;
	}

	ra_mutexEnter( &cctlData->processList->lock );

	ra_createRASTRING( &agentNameRASTR, legacyAgentName );

	process = ra_processFind( cctlData->processList, processId);
	/* Create the agent object */
	if(!process) {
		TPTP_LOG_INFO_MSG2(stateData, "Registering process %d %s", processId, agentName);
		process=ra_processCreate(cctlData->processList);
		process->pid=processId;
/* BEGIN:  235649 */
#if defined __linux__
		process->pid2=processId;
#endif
/* END: 235649 */
		process->isActive=TRUE;

		/* Create the agent */
		agent=ra_agentCreate(process, TRUE);
		agent->attached=FALSE;
	}
	else {
/* BEGIN:  235649 */
#if defined __linux__
		process->pid2=processId;
#endif
/* END: 235649 */

		/* Look for the agent object in the inactive list for the process */
		agent=ra_agentFindByProcess(process, &agentNameRASTR, FALSE);

		/* If the agent exists, make it active */
		if(agent) {
			ra_agentMakeActive(agent);
		}
		else {
			/* Create the agent */
			agent=ra_agentCreate(process, TRUE);
			agent->attached=FALSE;
		}
	}
	ra_copyRASTRING(&agent->agentName, &agentNameRASTR);

	ra_mutexExit( &cctlData->processList->lock );

	/* At this point, we don't have a reference to the agent, so its agentID is still unknown,
	   but we do have a token which we can use to get a reference to the agent */
	agent->agentID = -1;
	agent->agentToken = token;

	/* This function will fill in the agentType and agentUUID fields */
	getAgentTypeAndUUID( stateData, agent );

	/* If there is a client attached to this new agent inform it is active */
	if(agent->attached && agent->client) {
		ra_message_t *             message;
		ra_command_t *             command;
		tl_control_connection_t*   connectionEntry;
		client_connection_block_t* ccb;
		tptp_int32                 ret;

		/* retrieve the corresponding client information */
		connectionEntry = (tl_control_connection_t*) tableGet(stateData->controlConnectionTable, agent->client->clientID) ;
		if ( connectionEntry == NULL )
		{
			/* Unrecognized connection */
			/* TODO: Log an error */
			return -1;
		}
		ccb = (client_connection_block_t*) connectionEntry->implData;
		if ( ccb == NULL )
		{
			/* Missing client connection block */
			/* TODO: Log an error */
			return -1;
		}

		/* We need to add the agent to the client's agent list so that when the client exists the agents
		 * can be detached.
		 */
		ra_connectAgentToClient(agent, ccb);

		/* Send a command to the AC to attach based on the 'agent' structure */
		ret = getAgentByToken( stateData, ccb, agent );
		if ( ret == 0 )
		{
			message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
			command=ra_addCommandToMessage(message, NULL);

			command->tag=RA_AGENT_ACTIVE;
			command->info.agent_active.context=agent->context;
			command->info.agent_active.processId=agent->process->pid;
			ra_copyRASTRING(&command->info.agent_active.agent, &agent->agentName);
			ra_copyRASTRING(&command->info.agent_active.agentUUID, &agent->agentUUID);
			ra_copyRASTRING(&command->info.agent_active.agentType, &agent->agentType);
			ra_copyRASTRING(&command->info.agent_active.processUUID, &agent->process->uuid);
			
			ra_forwardMessage(message, ccb);
			ra_destroyMessage(message, TRUE);
		}
	}

	return 0;
}

/*******************************************************************************/ 

tptp_int32 handleAgentDeregistered( tl_state_data_t*           stateData, 
                                    tptp_string*               agentName, 
                                    tptp_uint32                token, 
                                    PID                        processId )
{
	cctl_state_data_t*  cctlData = (cctl_state_data_t*)stateData->implData;
	agent_t *           agent = NULL;
	process_t *         process = NULL;
	ra_string_t         agentNameRASTR;
	char                legacyAgentName[8192];         
	int                 ret;

	ret = findAgentName(agentName, legacyAgentName);
	if ( (ret == 0) || (ret == EOF) )
	{
		/* If the string wasn't in this format, we aren't interested in this agent */
		return 0;
	}

	ra_createRASTRING( &agentNameRASTR, legacyAgentName );

	ra_mutexEnter( &cctlData->processList->lock ); 

	process = ra_processFind( cctlData->processList, processId);
	/* Find the agent object, if it's one we know about */
	if(!process) {
		ra_mutexExit( &cctlData->processList->lock ); 
		TPTP_LOG_ERROR_MSG1(stateData, "Deregistering agent: couldn't find process (%d).", processId);
		return -1;
	} else {
		/* Look for this agent in our active list */
		agent=ra_agentFindByProcess(process, &agentNameRASTR, TRUE);
	}

	/* Did the active agent exist? */
	if(agent==NULL) {
		ra_mutexExit( &cctlData->processList->lock ); 
		TPTP_LOG_ERROR_MSG1(stateData, "Deregistering agent: couldn't find agent in process (%d).", processId );
		return -1;
	}

	TPTP_LOG_INFO_MSG1(stateData, "Deregistering agent %s. ", agent->agentUUID.data);

	if(agent->attached && agent->client) {
		ra_message_t*              forwardMessage;
		ra_command_t*              command;
		tl_control_connection_t*   connectionEntry;
		client_connection_block_t* ccb;

		/* retrieve the corresponding client information */
		connectionEntry = (tl_control_connection_t*) tableGet(stateData->controlConnectionTable, agent->client->clientID) ;
		if ( connectionEntry == NULL )
		{
			/* Unrecognized connection */
			ra_mutexExit( &cctlData->processList->lock ); 
			TPTP_LOG_ERROR_MSG1(stateData, "Deregistering agent: unrecognized connection Id (%d).", agent->client->clientID);
			return -1;
		}
		ccb = (client_connection_block_t*) connectionEntry->implData;
		if ( ccb == NULL )
		{
			/* Missing client connection block */
			ra_mutexExit( &cctlData->processList->lock ); 
			TPTP_LOG_ERROR_MSG1(stateData, "Deregistering agent: missing client connection block for Id (%d).", agent->client->clientID);
			return -1;
		}

		/* Formulate the message to forward to the client */
		forwardMessage=ra_createMessage(RA_CONTROL_MESSAGE, 0);
		command=ra_addCommandToMessage(forwardMessage, NULL);

		/* Fill in the details */
		command->tag=RA_AGENT_INACTIVE;
		command->info.agent_inactive.processId=agent->process->pid;
		ra_copyRASTRING(&command->info.agent_inactive.agentType, &agent->agentType);
		ra_copyRASTRING(&command->info.agent_inactive.agent, &agent->agentName);
		ra_copyRASTRING(&command->info.agent_inactive.agentUUID, &agent->agentUUID);
		ra_copyRASTRING(&command->info.agent_inactive.processUUID, &agent->process->uuid);

		/* Set the context */
		command->info.agent_inactive.context=agent->context;

		/* Forward the message */
		ra_forwardMessage(forwardMessage, ccb);
		ra_destroyMessage(forwardMessage, FALSE);

		/* Remove agent from client's agent list */
		TPTP_LOG_INFO_MSG1(stateData, "Removing agent %s. from client", agent->agentUUID.data);
		ra_disconnectAgentFromClient(agent, ccb);  /* 215083 */
	}

	/* Make the agent inactive */
	ra_agentMakeInactive(agent); /* Bug 71586 */

	/* If there are no more active agents for this process, determine if the process exited */
	if(agent->process->active_agent_count==0) {
		TPTP_LOG_DEBUG_MSG1(stateData, "No more agents for process %d, but it isn't being scrubbed here.", agent->process->pid);
	}

	ra_mutexExit( &cctlData->processList->lock ); 

	return 0;
}

/*******************************************************************************/ 

/* This command is being sent to an Agent Controller */
tptp_int32 translateAndForwardPeerRequestMonitor( tl_state_data_t*           stateData,
                                                  client_connection_block_t* ccb,
                                                  tptp_int32                 context, 
                                                  tptp_string*               peerHost,
                                                  tptp_string*               peerPort,
                                                  tptp_string*               peerAgentName,
                                                  tptp_string*               peerAgentPID,
                                                  tptp_string*               srcAgentName,
                                                  tptp_string*               srcAgentPID )
{
	cctl_state_data_t* cctlData = (cctl_state_data_t*)stateData->implData;
	ra_message_t*      forwardMessage;
	ra_command_t*      command;
	char               localAddr[4];
	char               peerAddr[4];
	char               legacyPeerAgentName[8192];         
	int                ret;

	if ( cctlData == NULL )
		return -1; /* TODO: indicate that this is unexpected */

	forwardMessage=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command=ra_addCommandToMessage( forwardMessage, NULL );

	command->tag=RA_CONTROLLER_REQUEST_MONITOR_PORT;

	/* Use the context of the target peer */
	command->info.controller_request_monitor_port.context=context;

	/* Store our port number */
	if ( cctlData->usePeerAttachPort )
	{
		command->info.controller_request_monitor_port.port = cctlData->peerAttachPort;
	}
	else
	{
		command->info.controller_request_monitor_port.port = cctlData->port;
	}

	/* Get our local host IP address */
	if ( cctlData->usePeerAttachAddress )
	{
		ra_createRASTRING3(&command->info.agent_request_monitor.node, (const char*)&cctlData->peerAttachAddress, sizeof(unsigned int));
	}
	else
	{
		char* localIPStr;

		if (getSocketIPStringIPv4M( ccb->sock, &localIPStr) < 0) {
			TPTP_LOG_ERROR_MSG(stateData, "Unable to get local IP address");
			return -1;
		}
		
		convertIPAddressStringToArrayIPv4M( localIPStr, localAddr);
		ra_createRASTRING3( &command->info.controller_request_monitor_port.node, localAddr, 4);

		/* We're done with this now, so let's free it */
		tptp_free( localIPStr ); 
		localIPStr = NULL;
	}
	
	/* Convert the peer port number */
	command->info.controller_request_monitor_port.port = strtoul(peerPort, 0, 10);

	/* Convert the peer address */
	convertIPAddressStringToArrayIPv4M( peerHost, peerAddr );
	ra_createRASTRING3( &command->info.controller_request_monitor_port.peerNode, peerAddr, 4 );

	/* Fill in the source agent's information */
	command->info.controller_request_monitor_port.peerProcessId = atoi(srcAgentPID);
	ra_createRASTRING(&command->info.controller_request_monitor_port.peerAgent, srcAgentName);

	/* The source agent name should have org.eclipse.legacy tacked on the front of it */
	/*   but we don't want that, so we'll strip it here */
	ret = findAgentName(peerAgentName, legacyPeerAgentName);
	if ( (ret == 0) || (ret == EOF) )
	{
		strcpy( legacyPeerAgentName, peerAgentName );
	}

	/* Fill in the peer agent's information */
	command->info.controller_request_monitor_port.processId = strtoul(peerAgentPID, NULL, 10);
	ra_createRASTRING(&command->info.controller_request_monitor_port.agent, legacyPeerAgentName);

	TPTP_LOG_DEBUG_MSG( stateData, "Forwarding peer monitor request to peer AC" );
	ra_forwardMessage(forwardMessage, ccb);
	ra_destroyMessage(forwardMessage, FALSE);

	return 0;
}

/*******************************************************************************/ 

/* This command is being sent to the client */
tptp_int32 translateAndForwardRequestMonitor( tl_state_data_t*           stateData,
                                              client_connection_block_t* ccb,
                                              tptp_int32                 context, 
                                              tptp_string*               peerHost,
                                              tptp_string*               peerPort,
                                              tptp_string*               peerAgentName,
                                              tptp_string*               peerAgentPID )
{
	cctl_state_data_t* cctlData = (cctl_state_data_t*)stateData->implData;
	char               localAddr[4];
	ra_message_t*      forwardMessage;
	ra_command_t*      command;

	if ( cctlData == NULL )
		return -1; /* TODO: indicate that this is unexpected */

	forwardMessage=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command=ra_addCommandToMessage( forwardMessage, NULL );

	command->tag=RA_CONTROLLER_REQUEST_MONITOR_PORT;

	/* Use the context of the target peer */
	command->info.controller_request_monitor_port.context=context;

	/* Store our port number */
	if ( cctlData->usePeerAttachPort )
	{
		command->info.controller_request_monitor_port.port = cctlData->peerAttachPort;
	}
	else
	{
		command->info.controller_request_monitor_port.port = cctlData->port;
	}

	/* Get our local host IP address */
	if ( cctlData->usePeerAttachAddress )
	{
		ra_createRASTRING3(&command->info.agent_request_monitor.node, (const char*)&cctlData->peerAttachAddress, sizeof(unsigned int));
	}
	else
	{
		char* localIPStr;

		if (getSocketIPStringIPv4M( ccb->sock, &localIPStr) < 0) {
			TPTP_LOG_ERROR_MSG(stateData, "Unable to get local IP address");
			return -1;
		}
		
		convertIPAddressStringToArrayIPv4M( localIPStr, localAddr);
		ra_createRASTRING3( &command->info.controller_request_monitor_port.node, localAddr, 4);

		/* We're done with this now, so let's free it */
		tptp_free( localIPStr ); 
		localIPStr = NULL;
	}

	/* Convert the peer port number */
	command->info.controller_request_monitor_port.port = strtoul(peerPort, 0, 10);

	/* Convert the peer address */
	convertIPAddressStringToArrayIPv4M( peerHost, command->info.controller_request_monitor_port.peerNode.data );
	command->info.controller_request_monitor_port.peerNode.length = 4;

	/* Fill in the peer agent's information */
	command->info.controller_request_monitor_port.peerProcessId = atoi(peerAgentPID);
	ra_createRASTRING(&command->info.controller_request_monitor_port.peerAgent, peerAgentName);

	TPTP_LOG_DEBUG_MSG( stateData, "Forwarding peer monitor request to client" );
	ra_forwardMessage(forwardMessage, ccb);
	ra_destroyMessage(forwardMessage, FALSE);

	return 0;
}

/*******************************************************************************/ 

tptp_int32 processCommand( tl_state_data_t*           stateData, 
                           client_connection_block_t* ccb, 
                           tptp_string*               cmd, 
                           tptp_int32                 sourceID, 
                           tptp_int32                 context, 
                           tptp_string*               interfaceID, 
                           tptp_string*               cmdName, 
                           tptp_list_t*               paramList )
{
	tptp_int32 ret;

	//TPTP_LOG_DEBUG_MSG3( stateData, "CCTL processing command (%s) from srcID %d, clientID %d",
	//	cmd, sourceID, ccb->clientID);

	if ( isEqualString( interfaceID, "org.eclipse.tptp.agentManager" ) )
	{
		/* Handle responses to commands we sent to the Agent Manager */
		if ( isEqualString( cmdName, "runningAgents" ) )
		{
			//we need to parse the running agents and then
			//formulate a response to the client.
			//the contextData will tell us how to process.
			ret = handleRunningAgents( stateData, ccb, context, paramList );
		}
		else if ( isEqualString( cmdName, "agentReference" ) )
		{
			//we need to parse the running agents and then
			//formulate a response to the client.
			//the contextData will tell us how to process.
			ret = handleAgentReference( stateData, ccb, context, paramList );
		}
		else if ( isEqualString( cmdName, "propertyList" ) )
		{
			//we need to reformat this as a response to the client
			ret = handlePropertyList( stateData, ccb, context, paramList );
		}
		else if ( isEqualString( cmdName, "shutdown" ) )
		{
			/* In this case, we are trying to send a shutdown request to a peer AC */
			/* The RAC did this using named pipes, not through the standard command protocol */
			HANDLE masterPipe = (HANDLE)-1;
			char buffer[8] = "SHUTDOWN";
			int bytesWritten = 0;
			int retryCount = 0;

			while((retryCount < 10) && ((int)masterPipe < 0)) {
				masterPipe = openWriteOnlyNamedPipe(RA_PIPE_NAMESPACE, RA_MASTER_ADDRESS, FALSE);
				if((int)masterPipe < 0) {
					SLEEP(1000);
					retryCount++;
				}
			}

			/* Assume AC is down if master named pipe cannot be opened */
			if((int)masterPipe < 0) {
				return 0;
			}

			writeToNamedPipe(masterPipe, buffer, 0, 8, &bytesWritten);

			cleanPipeUp(&masterPipe);

			return 0;
		} else if ( isEqualString( cmdName, "agentDeregistered" ) ) {
			/** Command is not supported by the Connect2AC code of ClientCTL. */
			TPTP_LOG_DEBUG_MSG2( stateData, "Client compatibility transport layer received a command that that is recognized (but unsupported). (iid: %s  cmd: %s)", "org.eclipse.tptp.agentManager", cmdName );
			
			return 0;			
		}
		else
		{
			/* Log this as an unrecognized command */
			TPTP_LOG_ERROR_MSG2( stateData, "CCTL Received unrecognized command: iid=%s, cmd=%s", interfaceID, cmdName );
			return -1;
		}
	}
	else if ( isEqualString( interfaceID, "org.eclipse.tptp.ac.peerMonitoring" ) )
	{
		/* If the AC tries to send a peerRequestMonitor command, 
		   we need to intercept and translate it to the old protocol */
		if ( isEqualString( cmdName, "peerRequestMonitor" ) )
		{
			tptp_string*  targetConnectionInfo = NULL;
			tptp_string*  targetAgentInfo = NULL;
			tptp_string*  sourceAgentInfo = NULL;
			tptp_string*  peerHost = NULL;
			tptp_string*  peerPort = NULL;
			tptp_string*  peerAgentName = NULL;
			tptp_string*  peerAgentPID = NULL;
			tptp_string*  srcAgentName = NULL;
			tptp_string*  srcAgentPID = NULL;
			int           ret;

			/* Get the information from the parameter list */
			getXmlFragmentParam( "targetConnectionInfo", paramList, &targetConnectionInfo );
			getXmlFragmentParam( "targetAgentInfo", paramList, &targetAgentInfo );
			getXmlFragmentParam( "sourceAgentInfo", paramList, &sourceAgentInfo );

			/* Parse these XML fragments to get the information we need from them */
			getXmlFragmentElementString( targetConnectionInfo, "targetConnectionInfo", "host", &peerHost );
			getXmlFragmentElementString( targetConnectionInfo, "targetConnectionInfo", "port", &peerPort );
			getXmlFragmentElementString( targetAgentInfo, "targetAgentInfo", "agentName", &peerAgentName );
			getXmlFragmentElementString( targetAgentInfo, "targetAgentInfo", "pid",       &peerAgentPID );
			getXmlFragmentElementString( sourceAgentInfo, "sourceAgentInfo", "agentName", &srcAgentName );
			getXmlFragmentElementString( sourceAgentInfo, "sourceAgentInfo", "pid",       &srcAgentPID );

			/* If any of these was NULL, we can't continue */
			if ( ! (targetConnectionInfo && targetAgentInfo && sourceAgentInfo && peerHost && 
			        peerPort && peerAgentName && peerAgentPID && srcAgentName && srcAgentPID) )
			{
				TPTP_LOG_ERROR_MSG( stateData, "One or more parameters was missing from the peerRequestMonitor command." );
				ret = -1;
			}
			else
			{
				ret= translateAndForwardPeerRequestMonitor( stateData,
				                                            ccb,
				                                            context,
													        peerHost,
			                                                peerPort,
					                                        peerAgentName,
					                                        peerAgentPID,
					                                        srcAgentName,
					                                        srcAgentPID );
			}

			/* Clean up */
			if (targetConnectionInfo)
				tptp_free(targetConnectionInfo);
			if (targetAgentInfo)
				tptp_free(targetAgentInfo); 
			if (sourceAgentInfo)
				tptp_free(sourceAgentInfo); 
			if (peerHost)
				tptp_free(peerHost);
			if (peerPort)
				tptp_free(peerPort);
			if (peerAgentName)
				tptp_free(peerAgentName);
			if (peerAgentPID)
				tptp_free(peerAgentPID);
			if (srcAgentName)
				tptp_free(srcAgentName);
			if (srcAgentPID)
				tptp_free(srcAgentPID);

			return ret;
		}
		else
		{
			/* Log this as an unrecognized command */
			TPTP_LOG_ERROR_MSG2( stateData, "Received unrecognized command: iid=%s, cmd=%s", interfaceID, cmdName );
			return -1;
		}
	}
	else if ( isEqualString( interfaceID, "org.eclipse.tptp.client.peerMonitoring" ) )
	{
		/* If the AC tries to send a requestMonitor command, 
		   we need to intercept and translate it to the old protocol */
		if ( isEqualString( cmdName, "requestMonitor" ) )
		{
			tptp_string*  peerConnectionInfo = NULL;
			tptp_string*  peerAgentInfo = NULL;
			tptp_string*  peerHost = NULL;
			tptp_string*  peerPort = NULL;
			tptp_string*  peerAgentName = NULL;
			tptp_string*  peerAgentPID = NULL;
			int           ret;

			/* Get the information from the parameter list */
			getXmlFragmentParam( "sourceConnectionInfo", paramList, &peerConnectionInfo );
			getXmlFragmentParam( "targetAgentInfo", paramList, &peerAgentInfo );

			/* Parse these XML fragments to get the information we need from them */
			getXmlFragmentElementString( peerConnectionInfo, "sourceConnectionInfo", "host", &peerHost );
			getXmlFragmentElementString( peerConnectionInfo, "sourceConnectionInfo", "port", &peerPort );
			getXmlFragmentElementString( peerConnectionInfo, "targetAgentInfo", "agentName", &peerAgentName );
			getXmlFragmentElementString( peerConnectionInfo, "targetAgentInfo", "pid", &peerAgentPID );

			/* If any of these was NULL, we can't continue */
			if ( ! (peerConnectionInfo && peerAgentInfo && peerHost && 
			        peerPort && peerAgentName && peerAgentPID ) )
			{
				TPTP_LOG_ERROR_MSG( stateData, "One or more parameters was missing from the requestMonitor command." );
				ret = -1;
			}
			else
			{
				ret= translateAndForwardRequestMonitor( stateData,
				                                        ccb,
				                                        context,
													    peerHost,
			                                            peerPort,
					                                    peerAgentName,
					                                    peerAgentPID );
			}

			/* Clean up */
			if (peerConnectionInfo)
				tptp_free(peerConnectionInfo);
			if (peerAgentInfo)
				tptp_free(peerAgentInfo); 
			if (peerHost)
				tptp_free(peerHost);
			if (peerPort)
				tptp_free(peerPort);
			if (peerAgentName)
				tptp_free(peerAgentName);
			if (peerAgentPID)
				tptp_free(peerAgentPID);

			return ret;
		}
		else
		{
			/* Log this as an unrecognized command */
			TPTP_LOG_ERROR_MSG2( stateData, "Received unrecognized command: iid=%s, cmd=%s", interfaceID, cmdName );
			return -1;
		}
	}
	else if ( isEqualString( interfaceID, "org.eclipse.tptp.legacy" ) )
	{
		/* Handle the response to our getTypeandUUID query */
		if ( isEqualString( cmdName, "agentTypeAndUUID" ) )
		{
			ret = handleAgentDetails( stateData, ccb, context, paramList );
		}
		/* Handle the response to waitForDataFlush */
		else if ( isEqualString( cmdName, "waitForDataFlushComplete" ) )
		{
			generic_context_t *contextStruct;

			ret = baseTL_getContextData( stateData, context, (void**) &contextStruct );
			if ( ret == 0 )
			{
				if ( contextStruct->contextDataType != WAIT_FOR_FLUSH_CONTEXT_TYPE )
				{
					TPTP_LOG_ERROR_MSG( stateData, "Bad context data type associated with waitForDataFlush response" );
				}
				else
				{
					generic_wait_context_t* waitContext = (generic_wait_context_t*)contextStruct;

					tptp_postSemaphore( &waitContext->completionSemaphore );
				}
			}
		}
		/* Handle legacy commands that have simply been passed through */
		else if ( isEqualString( cmdName, "customCommand" ) )
		{
			ra_message_t* message;
			ra_command_t* command;
			tptp_string*  data = NULL;
			int           dataStrLen;
			cctl_state_data_t*  cctlData;
			agent_t* agent;
			int len;
			char *buffer;

			cctlData = (cctl_state_data_t*)ccb->stateData->implData;

			getStringParam( "message", paramList, &data );

			agent = ra_agentFindByAgentID( cctlData->processList, sourceID );
			if ( agent == NULL )
			{
				/* TODO: Report error */
				return FALSE;
			}

			message = ra_createMessage(RA_CONTROL_MESSAGE, 0);
			command = ra_addCommandToMessage(message, NULL);
			command->tag = RA_CUSTOM_COMMAND;
			command->info.custom_command.context = context;
			ra_copyRASTRING( &(command->info.custom_command.agent), &agent->agentName );
			command->info.custom_command.processId = agent->process->pid;
			
			/* Convert the base64 data to binary */
			dataStrLen = strlen(data);

			/* Calculate the size of buffer required */
			len = tptp_decodeBase64( data, dataStrLen, NULL, 0);

			/* Bug 191075 begins */
			buffer = (char*)tptp_malloc(sizeof(char) * len);
			tptp_decodeBase64(data, dataStrLen, buffer, len);

			if(buffer[len - 1] == 0) { /* Trim the null if found */
				command->info.custom_command.message.data = (char*)tptp_malloc(sizeof(char) * (len - 1));
				memcpy(command->info.custom_command.message.data, buffer, len - 1);
				command->info.custom_command.message.length = len - 1; 
				tptp_free(buffer);
			}
			else {
				command->info.custom_command.message.data = buffer;
				command->info.custom_command.message.length = len; 
			}
			/* Bug 191075 ends */

			if (data != NULL)
			{
				tptp_free(data);
			}

			ra_forwardMessage(message, ccb);
			ra_destroyMessage(message, TRUE);

			return TRUE;
		}
		else if ( isEqualString( cmdName, "binaryCustomCommand" ) )
		{
			ra_message_t* message;
			ra_command_t* command;
			tptp_string*  data = NULL;
			int           dataStrLen;
			cctl_state_data_t*  cctlData;
			agent_t* agent;
			int len;

			cctlData = (cctl_state_data_t*)ccb->stateData->implData;

			getStringParam( "message", paramList, &data );

			agent = ra_agentFindByAgentID( cctlData->processList, sourceID );
			if ( agent == NULL )
			{
				/* TODO: Report error */
				return FALSE;
			}

			message = ra_createMessage(RA_CONTROL_MESSAGE, 0);
			command = ra_addCommandToMessage(message, NULL);
			command->tag = RA_BINARY_CUSTOM_COMMAND;
			command->info.custom_command.context = context;

			/* Change the context id */
			/* 179443 - Initially the idea was to set the context to always
						be the context of the launch.  This must only be the
						case of error conditions as we regressed the WTE
						folks because they originate commands at the agent
						and we were changing the context here
			*/

			/* This bunch of operators is obviously a bit hard to parse visually. 
			 *	   What it says is this:
			 *
			 *        if (this is not a custom command) OR 
			 *           (it is a custom command, but the context is zero)
			 *        {
			 *           use the context associated with the agent
			 *        }
			 */
			if (  ( ( command->tag != RA_CUSTOM_COMMAND ) && 
					( command->tag != RA_BINARY_CUSTOM_COMMAND) ) ||
				  ( ( (command->tag == RA_CUSTOM_COMMAND) || 
					  (command->tag == RA_BINARY_CUSTOM_COMMAND) ) && 
					  (command->info.set_nv_pair.context == 0))) 
			{
				command->info.set_nv_pair.context=agent->context;
			}

			ra_copyRASTRING( &(command->info.custom_command.agent), &agent->agentName );
			command->info.custom_command.processId = agent->process->pid;

			/* Convert the base64 data to binary */
			dataStrLen = strlen(data);

			/* Calculate the size of buffer required */
			len = tptp_decodeBase64(data, dataStrLen, NULL, 0); 
			command->info.custom_command.message.data = (char*)tptp_malloc(sizeof(char) * len);
			tptp_decodeBase64( data, dataStrLen, command->info.custom_command.message.data, len );
			command->info.custom_command.message.length = len;

			if (data != NULL)
			{
				tptp_free(data);
			}

			ra_forwardMessage(message, ccb);
			ra_destroyMessage(message, TRUE);

			return TRUE;
		}
		else if ( isEqualString( cmdName, "setNameValuePair" ) )
		{
			ra_message_t* message;
			ra_command_t* command;
			tptp_string*  name  = NULL;
			tptp_string*  type  = NULL;
			tptp_string*  value = NULL;
			cctl_state_data_t*  cctlData;
			agent_t* agent;

			cctlData = (cctl_state_data_t*)ccb->stateData->implData;

			agent = ra_agentFindByAgentID( cctlData->processList, sourceID );
			if ( agent == NULL )
			{
				/* TODO: Report error */
				return FALSE;
			}

			getStringParam( "name",  paramList, &name  );
			getStringParam( "type",  paramList, &type  );
			getStringParam( "value", paramList, &value );

			message = ra_createMessage(RA_CONTROL_MESSAGE, 0);
			command = ra_addCommandToMessage(message, NULL);
			command->tag = RA_CUSTOM_COMMAND;
			command->info.set_nv_pair.context = context;
			ra_copyRASTRING( &(command->info.set_nv_pair.agent), &agent->agentName );

			command->info.custom_command.processId = agent->process->pid;
			ra_createRASTRING( &command->info.set_nv_pair.type,  type );
			ra_createRASTRING( &command->info.set_nv_pair.name,  name );
			ra_createRASTRING( &command->info.set_nv_pair.value, value );

			if (name != NULL)
				tptp_free(name);
			if (type != NULL)
				tptp_free(type);
			if (value != NULL)
				tptp_free(value);

			ra_forwardMessage(message, ccb);
			ra_destroyMessage(message, TRUE);

			return TRUE;
		}
		else if ( isEqualString( cmdName, "errorString" ) )
		{
			ra_message_t* message;
			ra_command_t* command;
			tptp_uint32 errorSeverity = 0;
			tptp_string*  errorMsg = NULL;
			tptp_string*  errorMsgId = NULL;
			int           errorMsgStrLen;
			int           errorMsgIdStrLen;
			int len;

			cctl_state_data_t*  cctlData;
			agent_t* agent;

			cctlData = (cctl_state_data_t*)ccb->stateData->implData;

			agent = ra_agentFindByAgentID( cctlData->processList, sourceID );
			if ( agent == NULL )
			{
				/* TODO: Report error */
				return FALSE;
			}

			getIntegerParam("severity", paramList, &errorSeverity);
			getStringParam("messageId", paramList, &errorMsgId);
			getStringParam("message", paramList, &errorMsg);

			message = ra_createMessage(RA_CONTROL_MESSAGE, 0);
			command = ra_addCommandToMessage(message, NULL);
			command->tag = RA_ERROR_STRING;
			command->info.error_string.context = context;
			ra_copyRASTRING( &(command->info.error_string.agent), &agent->agentName );
			command->info.error_string.processId = agent->process->pid;

			/* Convert the base64 message to binary */
			errorMsgIdStrLen = strlen(errorMsgId);

			/* Calculate the size of buffer required */
			len = tptp_decodeBase64(errorMsgId, errorMsgIdStrLen, NULL, 0); 
			command->info.error_string.messageId.data = (char*)tptp_malloc(sizeof(char) * len);
			tptp_decodeBase64(errorMsgId, errorMsgIdStrLen, command->info.error_string.messageId.data, len);
			command->info.error_string.messageId.length = len;

			errorMsgStrLen = strlen(errorMsg);

			/* Calculate the size of buffer required */
			len = tptp_decodeBase64(errorMsg, errorMsgStrLen, NULL, 0); 
			command->info.error_string.message.data = (char*)tptp_malloc(sizeof(char) * len);
			tptp_decodeBase64(errorMsg, errorMsgStrLen, command->info.error_string.message.data, len);
			command->info.error_string.message.length = len;

			if (errorMsgId != NULL)
				tptp_free(errorMsgId);
			if (errorMsg != NULL)
				tptp_free(errorMsg);

			ra_forwardMessage(message, ccb);
			ra_destroyMessage(message, TRUE);

			return TRUE;
		}
		else
		{
			/* Log this as an unrecognized command */
			TPTP_LOG_ERROR_MSG2( stateData, "CCTL Received unrecognized command: iid=%s, cmd=%s", interfaceID, cmdName );
			return -1;
		}
	}
	else if ( isEqualString( interfaceID, AGENTMGR_AGENT_EVENTS_IID ) )
	{
		if ( isEqualString( cmdName, "agentRegistered" ) )
		{
			PID          processID;
			tptp_uint32  token;
			tptp_string* agentName;

			ret = getPIDParam( "processID", paramList, &processID );
			if ( ret != 0 )
			{
				TPTP_LOG_ERROR_MSG( stateData, "Missing processID parameter in agentRegistered notification event." );
				return TPTP_CMD_MISSING_ARG;
			}

			ret = getIntegerParam( "agentToken", paramList, &token );
			if ( ret != 0 )
			{
				TPTP_LOG_ERROR_MSG( stateData, "Missing agentToken parameter in agentRegistered notification event." );
				return TPTP_CMD_MISSING_ARG;
			}

			ret = getStringParam( "agentName", paramList, &agentName );
			if ( ret != 0 )
			{
				TPTP_LOG_ERROR_MSG( stateData, "Missing agentName parameter in agentRegistered notification event." );
				return TPTP_CMD_MISSING_ARG;
			}

			ret = handleAgentRegistered( stateData, agentName, token, processID );

			tptp_free( agentName );
		}
		else if ( isEqualString( cmdName, "agentDeregistered" ) )
		{
			PID          processID;
			tptp_uint32  token;
			tptp_string* agentName;

			ret = getPIDParam( "processID", paramList, &processID );
			if ( ret != 0 )
			{
				TPTP_LOG_ERROR_MSG( stateData, "Missing processID parameter in agentDeregistered notification event." );
				return TPTP_CMD_MISSING_ARG;
			}

			ret = getIntegerParam( "agentToken", paramList, &token );
			if ( ret != 0 )
			{
				TPTP_LOG_ERROR_MSG( stateData, "Missing agentToken parameter in agentDeregistered notification event." );
				return TPTP_CMD_MISSING_ARG;
			}

			ret = getStringParam( "agentName", paramList, &agentName );
			if ( ret != 0 )
			{
				TPTP_LOG_ERROR_MSG( stateData, "Missing agentName parameter in agentDeregistered notification event." );
				return TPTP_CMD_MISSING_ARG;
			}

			ret = handleAgentDeregistered( stateData, agentName, token, processID );

			tptp_free( agentName );
		}
		/* This is an event interface, so we should ignore unrecognized commands */
	}
	else if ( isEqualString( interfaceID, "org.eclipse.tptp.processController" ) ) 
	{
		if ( isEqualString( cmdName, "processStarted" ) )
		{
			ret = handleProcessStarted( stateData, ccb, context, paramList);
		}
		else if ( isEqualString( cmdName, "processExitedEvent" ) )
		{
			ret = handleProcessExitEvent( stateData, ccb, context, paramList);
		}
		else
		{
			/* Log this as an unrecognized command */
			TPTP_LOG_ERROR_MSG2( stateData, "CCTL Received unrecognized command: iid=%s, cmd=%s", interfaceID, cmdName );
			return -1;
		}
	}
	else if ( isEqualString( interfaceID, "org.eclipse.tptp.dataProvider" ) )
	{
		if ( isEqualString( cmdName, "dataPathEstablished" ) )
		{
			ret = handleDataPathEstablished( stateData, ccb, context, paramList );
		}
		else
		{
			/* Log this as an unrecognized command */
			TPTP_LOG_ERROR_MSG2( stateData, "CCTL Received unrecognized command: iid=%s, cmd=%s", interfaceID, cmdName );
			return -1;
		}
	}
	else if ( isEqualString( interfaceID, TPTP_ERROR_IID ) )
	{
		if ( isEqualString( cmdName, "TPTP_Error" ) )
		{
			tptp_string* errorInfo;
			generic_context_t *contextStruct;

			ret = baseTL_getContextData( stateData, context, (void**) &contextStruct );
			if ( ret == 0 )
			{
				switch ( contextStruct->contextDataType )
				{
					case GET_AGENT_CONTEXT_TYPE:
					case START_PROCESS_CONTEXT_TYPE:
					case GET_TYPE_CONTEXT_TYPE:
					case ESTABLISH_DATA_PATH_CONTEXT_TYPE:
						{
							generic_wait_context_t* waitContext = (generic_wait_context_t*)contextStruct;

							waitContext->status = -1;
							tptp_postSemaphore( &waitContext->completionSemaphore );
						}
						break;

					case GET_PROPLIST_CONTEXT_TYPE:
						{
							get_property_list_context_t* gplContext = (get_property_list_context_t*)contextStruct;

							/* TODO: Send a response to the client */
							tptp_free( gplContext );
						}
						break;
					default:
						break;
				}
			}
		
			ret = getStringParam( "ErrInfo", paramList, &errorInfo );
			if ( ret == 0 )
			{
				/* Log the error along with the failure explanation */
				TPTP_LOG_WARNING_MSG1( stateData, "Client compatibility transport layer received an error command: %s", errorInfo );
				tptp_free( errorInfo );
			}
			else
			{
				/* This is unexpected, but we'd at least like to know that this happened. */
				TPTP_LOG_WARNING_MSG( stateData, "Client compatibility transport layer received an error command: additional information is not available." );
			}

			return -1;
		}
		else
		{
			/* We're already in error handling code, but we don't recognize the error command
			       nothing we do here is likely to be helpful, but we'll log this just so someone
			       knows we got here */
			TPTP_LOG_WARNING_MSG1( stateData, "Client compatibility transport layer received an unrecognized error command: %s", cmdName );
			return -1;
		}
	} else if ( isEqualString( interfaceID, EVENT_PROVIDER_IID))
	{
		if(isEqualString( cmdName, "listenerAccepted")) {

			/** Command is not supported by the Connect2AC code of ClientCTL. */
			TPTP_LOG_DEBUG_MSG2( stateData, "Client compatibility transport layer received a command that that is recognized (but unsupported). (iid: %s  cmd: %s)", EVENT_PROVIDER_IID, cmdName );
			return 0;			
			
		} else {
			/* Log this as an unrecognized interface */
			TPTP_LOG_ERROR_MSG2( stateData, "Client compatibility transport layer received an unrecognized command, iid: %s  cmd: %s", EVENT_PROVIDER_IID, cmdName );
			return -1;			
		}
	}
	else
	{
		/* Log this as an unrecognized interface */
		TPTP_LOG_ERROR_MSG2( stateData, "Received command for unrecognized interface: iid=%s, cmd=%s", interfaceID, cmdName );
		return -1;
	}
	
	return 0;
}

