/**********************************************************************
* Copyright (c) 2006, 2009 IBM Corporation and others.
* 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
* $Id: secureServer.c,v 1.11 2009/07/10 00:11:34 jwest Exp $
* 
* Contributors: 
* IBM - Initial API and implementation
**********************************************************************/

#include "secureServer.h"
#include "RACClientSupport.h"

#define CREATECLIENT_METHOD_NAME "createClient0"

/* functions */
tptp_int32 registerSecuredServer(tptpJavaJob *job);
tptp_int32 createSecuredServer(tptpJavaJob *job);

THREAD_USER_FUNC_RET_TYPE processJavaClientRequest(LPVOID args);

tptp_uint32 startSecuredServer(tl_state_data_t* stateData, TID *tid) {
	int rc;

	/* Save the global state in a place where Java callbacks can get it */
	g_stateDataForJavaCallback = stateData;
		
	rc = tptpSubmitJavaJob(stateData, registerSecuredServer, FALSE, NULL);

	/* Register and start the server */
	if (rc == 0) {
		/* Submit our secure server as a Java job */
		if (tptpSubmitJavaJob(stateData, createSecuredServer, TRUE, tid)) {
			TPTP_LOG_ERROR_MSG(stateData, "Failure creating Secure Server");
		}
		else {
/*			TPTP_LOG_INFO_MSG(stateData, "Successfully creating Secure Server");*/
		}
	}
	else {
		TPTP_LOG_ERROR_MSG(stateData, "Failure registering Secure Server");
	}

	return 0;
}

tptp_int32 registerSecuredServer(tptpJavaJob *job) {
	jclass chClass;
	cctl_state_data_t*	cctlData;
	tl_state_data_t*	stateData;

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


	chClass = fndclsid(job -> env, CLASS_CONNECTION_HANDLER);
	/* register native method */
	if (chClass) {
        int result;
        char mname[100];
        char sig[100];
        JNINativeMethod nmethod;

#ifdef __OS400__
	#pragma convert(819)
#endif
        strcpy(mname, CREATECLIENT_METHOD_NAME);
        strcpy(sig, METHOD_SIGNATURE_CLIENT_HANDLER);
#ifdef __OS400__
	#pragma convert(0)
#elif defined MVS
        __etoa(mname);
        __etoa(sig);
#endif
        nmethod.name = mname;
        nmethod.signature = sig;

        nmethod.fnPtr = (void *) Java_org_eclipse_hyades_internal_collection_framework_ConnectionHandlerImpl_createClient0;

        result = ENV(job->env)->RegisterNatives(ENVPARM(job->env) chClass, &nmethod, 1);

        if (result < 0) {
            ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
            TPTP_LOG_SEVERE_MSG1(stateData, "Could not register native method createClient0 for class %s", CLASS_CONNECTION_HANDLER);
        }
    }
    else {
        ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
        TPTP_LOG_SEVERE_MSG1(stateData, "Could not find class %s to register native method", CLASS_CONNECTION_HANDLER);
    }

    return 0;

}

/* Create a secured server */
tptp_int32 createSecuredServer(tptpJavaJob *job) {
	jclass classSecuredServer;
	jclass classSecuredServerParameters;
	jclass classString;
	jthrowable jexc;

	tptp_uint32		securedPort;
	tptp_string		*keystore;
	tptp_string		*keystorePassword;

	cctl_state_data_t*	cctlData;
	tl_state_data_t*	stateData;

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

	/* Fill up the required variables */
	securedPort = cctlData->securedPort;
	keystore = cctlData->keystore;
	keystorePassword = cctlData->keystorePassword;


	/* Locate our Server configuration class */
	classSecuredServer = getcnvcls(job->env, CLASS_SECURED_SERVER, strlen(CLASS_SECURED_SERVER));
	classSecuredServerParameters = getcnvcls(job->env, CLASS_SECURED_SERVER_PARAMETERS, strlen(CLASS_SECURED_SERVER_PARAMETERS));
	classString = getcnvcls(job->env, CLASS_STRING, strlen(CLASS_STRING));

	if(classSecuredServer && classSecuredServerParameters && classString) {
		jobject objectSecuredServerParameters;
		jobject objectSecuredServer;

		/* Create an instance of the configuration class using the default constructor */
		objectSecuredServerParameters = ENV(job->env)->AllocObject(ENVPARM(job->env) classSecuredServerParameters);
		objectSecuredServer = ENV(job->env)->AllocObject(ENVPARM(job->env) classSecuredServer);
		/* Need a global reference for shutting down the secured server */
		g_objectSecuredServer = ENV(job -> env) -> NewGlobalRef(ENVPARM(job -> env) objectSecuredServer);

		if(objectSecuredServer && objectSecuredServerParameters) {
			jmethodID method;

			/* Lookup and invoke each method in turn */
			method = getmthdid(job -> env, classSecuredServerParameters, "setPort", "(I)V");
			if(method) {
				ENV(job->env)->CallVoidMethod(ENVPARM(job->env) objectSecuredServerParameters, method, securedPort);
/*				TPTP_LOG_INFO_MSG1(stateData, "Secured port set to %d", securedPort); */
			}
			else {
				ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
				TPTP_LOG_SEVERE_MSG(stateData, "Could not get ID for method setPort()");
			}

			method = getmthdid(job -> env, classSecuredServerParameters, "setKeystoreFile", "(Ljava/lang/String;)V");
			if(method) {
				jstring value;
				value = getnewstrutf(job -> env, keystore);
				ENV(job->env)->CallVoidMethod(ENVPARM(job->env) objectSecuredServerParameters, method, value);
			}
			else {
				ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
				TPTP_LOG_SEVERE_MSG(stateData, "Could not get ID for method setKeystoreFile()");
			}

			method = getmthdid(job -> env, classSecuredServerParameters, "setKeystorePassword", "(Ljava/lang/String;)V");
			if(method) {
				jstring value;
				value = getnewstrutf(job -> env, keystorePassword);
				ENV(job->env)->CallVoidMethod(ENVPARM(job->env) objectSecuredServerParameters, method, value);
			}
			else {
				ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
				TPTP_LOG_SEVERE_MSG(stateData, "Could not get ID for method setKeystorePassword()");
			}

			method = getmthdid(job -> env, classSecuredServerParameters, "setKeystoreManager", "(Ljava/lang/String;)V");
			if(method) {
				jstring value;
				value = getnewstrutf(job -> env, CLASS_KEYSTORE_MANAGER);
				ENV(job->env)->CallVoidMethod(ENVPARM(job->env) objectSecuredServerParameters, method, value);
			}
			else {
				ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
				TPTP_LOG_SEVERE_MSG(stateData, "Could not get ID for method setKeystoreManager()");
			}

			/* OK, we are good to start our server */
			method = getmthdid(job -> env, classSecuredServer, "init", METHOD_SIGNATURE_SECURED_SERVER_PARAMETERS);
			if(method) {
				ENV(job->env)->CallVoidMethod(ENVPARM(job->env) objectSecuredServer, method, objectSecuredServerParameters);

				/* This is to detect the exception on AS/400 when setWantClientAuth() is called. */
				if (jexc = ENV(job -> env) -> ExceptionOccurred(ENVPARM1(job -> env))) {
					ENV(job -> env)->ExceptionDescribe(ENVPARM1(job -> env));
					ENV(job -> env)->ExceptionClear(ENVPARM1(job -> env));
					TPTP_LOG_SEVERE_MSG(stateData, "Exception in init()");
				}

				method = getmthdid(job -> env, classSecuredServer, "run", "()V");
				if(method) {
					TPTP_LOG_INFO_MSG1(stateData, "Secured server started listening on port %d", securedPort);
					ENV(job->env)->CallVoidMethod(ENVPARM(job->env) objectSecuredServer, method);
				}
				else {
					ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
					TPTP_LOG_SEVERE_MSG(stateData, "Could not get ID for method run()");
				}
			}
			else {
				ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
				TPTP_LOG_SEVERE_MSG(stateData, "Could not get ID for method init");
			}
        }
		else if (objectSecuredServerParameters == (int)NULL) {
			ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
			TPTP_LOG_SEVERE_MSG1(stateData, "Could not allocate object of class %s", CLASS_SECURED_SERVER_PARAMETERS);
		}
		else if (objectSecuredServer == (int)NULL) {
			ENV(job->env)->ExceptionClear(ENVPARM1(job->env));
			TPTP_LOG_SEVERE_MSG1(stateData, "Could not allocate object of class %s", CLASS_SECURED_SERVER);
		}
	}
	else {
		TPTP_LOG_SEVERE_MSG(stateData, "Could not find a java class. Server will not be secure.");
	}

	return 0;
}

/*
 * Class:     org_eclipse_hyades_internal_collection_framework_ConnectionHandlerImpl
 * Method:    createClient0
 * Signature: (Lorg/eclipse/hyades/internal/collection/framework/ClientHandlerImpl;)I
 */
JNIEXPORT jint JNICALL Java_org_eclipse_hyades_internal_collection_framework_ConnectionHandlerImpl_createClient0(JNIEnv *jenv, jobject jobj, jobject cHandler) {
	client_connection_block_t* clientConnectionBlock;
	tptp_uint32			connectionID;
	jint				rc = 0;
	tl_state_data_t*	stateData = g_stateDataForJavaCallback;
	TID					threadId;
	HANDLE				threadHandle;

	if ( stateData == NULL )
	{
		return -1;
	}

	/* set up the data block for each request */
	clientConnectionBlock = createClientConnectionBlock(0, stateData);

	/* Ask the AC for a new connection */
	rc = (jint)stateData->ac.addConnection( stateData->ac.cmo, stateData->transportID, &connectionID );
	if ( rc != 0 )
	{
		#if defined(__linux__) && defined(__s390x__)
			TPTP_LOG_ERROR_MSG1( stateData, "Error adding Agent Controller connection for Java socket connection: %lu", (unsigned long)cHandler );
		#else
			TPTP_LOG_ERROR_MSG1( stateData, "Error adding Agent Controller connection for Java socket connection: %u", (unsigned int)cHandler );
		#endif
		return rc;
	}

	/* Add this connection to a list maintained by the base TL implementation */
	baseTL_addControlConnectionEntry( stateData, connectionID, (void*)clientConnectionBlock );

	/* Fill in some details of the ccb */
	clientConnectionBlock->clientID = connectionID;
	clientConnectionBlock->connectionType = CCTL_JAVA_SOCKET;
	clientConnectionBlock->javaObj = ENV(jenv)->NewGlobalRef(ENVPARM(jenv) cHandler);
	clientConnectionBlock->secured = TRUE;

	/* go create a new thread to process each incoming connection request */
	tptpStartThread(processJavaClientRequest, (LPVOID)clientConnectionBlock, &threadId, &threadHandle) ;
	CLOSE_THREAD_HANDLE(threadHandle);
	TPTP_LOG_INFO_MSG(stateData, "Java client handling thread started");

	return 0;
}
