/*
 * GNU.FREE 2002
 *
 * Copyright (c) 1999, 2000, 2001, 2002
 * The Free Software Foundation (www.fsf.org)
 *
 * GNU.FREE Co-ordinator: Jason Kitcat <jeep@free-project.org>
 *
 * GNU site: http://www.gnu.org/software/free/
 * 
 * FREE e-democracy site: http://www.free-project.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program (COPYING); if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
 
package Free;

import java.io.*;
import java.net.*;
import java.util.*;

import Free.util.*;

/**
 * Comms is the key class for launching communications with servers.
 *
 * The class launches a thread for TCP/IP communications and also contains
 * the methods for building packets. These packets are then held in
 * <code>message</code> for retrieval by <code>TCPClient</code>
 *
 * @version 1.4 8 September 2001
 * @author Jason Kitcat
 */
public class Comms {

	private Thread talker;
	protected TimeOutThread timer;

	/** constant to hold address of totaller server */
//#ifdef INSTALL
	protected static final String T_address = "huxley.swingdigital.com";
	/** constant to hold address of electoral roll server */
//#ifdef INSTALL
	protected static final String ER_address = "gibson.swingdigital.com";
	/** constant to store value being used as GNU.FREE port number
	 * for security reasons I recommend this is changed regularly
	 */
//#ifdef INSTALL
	protected static final int freePort = 1111; // XXXX
	/** port number for RTServer */
//#ifdef INSTALL
	protected static final int freeRTPort = 1122; // XXXX
	/** stores total packet data while waiting to be sent */
	protected Vector totalData = new Vector(1);
	/** keeps track of where we are in the Vector */
	protected int tdCount;
	
	/** For returning data from <code>TCPClient</code>. Not currently used. */
	private String fromServer;
	/** Holds the check time/date stamp for FREE security protocol */
	private Date check;
	/** Holds the pseudo-AuthKey during Totals check process when in Regional mode */
	private String checkKey;
	/** A string holding the packet to be initially sent to a server */
	private String message;
	/** Stores the type of packet being sent, using the same flags as for
	 * section 1 of a FREE packet. This is used to determine which server
	 * to send <code>message</code> to.
	 */
	private char type;
	/** Vector to store TCPClient threads */
	protected Vector threadVect;
	/** Vector to store TimeOut threads */
	protected Vector threadTimeOut;
	
	/* Store session information */
	protected Vector session_data;
	protected Vector client_hmac_keys;
	protected Vector server_hmac_keys;
	protected Vector client_hmac_seeds;
	protected Vector server_hmac_seeds;
	protected Vector session_count;
	protected Vector message_twos;
	
	/* constructor */
	protected Comms() {
	
		threadVect = new Vector(1000);
		threadTimeOut = new Vector(1000);
		
		session_data = new Vector(1000);
		client_hmac_keys = new Vector(1000);
		server_hmac_keys = new Vector(1000);
		client_hmac_seeds = new Vector(1000);
		server_hmac_seeds = new Vector(1000);
		session_count = new Vector(1000);
		message_twos = new Vector(1000);
		// unneccessary but just in case!
		message = "";
		fromServer = "";
		checkKey = "";
	
	}

	 /**
	  * adds entries to Vectors to keep track of session_data
	  *
	  * @param vectIndex  Index of the vector information
	  * @param session_key  The encryption session_key to index by
	  * @param client_hmac_key  Client HMAC key
	  * @param server_hmac_key  Server HMAC key
	  * @param client_hmac_key  Client HMAC seed
	  * @param server_hmac_key  Server HMAC seed
	  * @param session_counter  Packet counter
	  */
	  protected void setSession(int vectIndex, String session_key, String client_hmac_key, String server_hmac_key, String client_hmac_seed, String server_hmac_seed, int session_counter, String message_two) {
	  
	  	session_data.insertElementAt(session_key, vectIndex);
	  	client_hmac_keys.insertElementAt(client_hmac_key, vectIndex);
	  	server_hmac_keys.insertElementAt(server_hmac_key, vectIndex);
	  	client_hmac_seeds.insertElementAt(client_hmac_seed, vectIndex);
	  	server_hmac_seeds.insertElementAt(server_hmac_seed, vectIndex);
	  	session_count.insertElementAt(new Integer(session_counter).toString(), vectIndex);
	  	message_twos.insertElementAt(message_two, vectIndex);
	  	
	  } //EOF setSession

	
	 /**
	  * tries to find matching session data.
	  *
	  * @param session_key
	  * @return  A string array with the results of the match
	  */
	  protected String[] getSession(int vectIndex) {

	  	String[] r = new String[] {"","","","","","",""};
	  	
		r[0] = client_hmac_keys.elementAt(vectIndex).toString(); // collect data
		r[1] = server_hmac_keys.elementAt(vectIndex).toString();
		r[2] = client_hmac_seeds.elementAt(vectIndex).toString();
		r[3] = server_hmac_seeds.elementAt(vectIndex).toString();
		r[4] = session_count.elementAt(vectIndex).toString();
		r[5] = session_data.elementAt(vectIndex).toString();
		r[6] = message_twos.elementAt(vectIndex).toString();
		client_hmac_keys.removeElementAt(vectIndex);  // clear data
		server_hmac_keys.removeElementAt(vectIndex);
		client_hmac_seeds.removeElementAt(vectIndex);
		server_hmac_seeds.removeElementAt(vectIndex);
		session_count.removeElementAt(vectIndex);
		session_data.removeElementAt(vectIndex);
		message_twos.removeElementAt(vectIndex);

	  	return r;
	  
	  } //EOF getSession


	/* simple acessor method */
	protected void setFromServer(String x) {
	
		fromServer = x;
	
	}
	
	/* simple acessor method */
	protected String getCheckKey() {
	
		return checkKey;
	
	}

	/* simple acessor method */
	protected void setCheckKey(String x) {
	
		checkKey = x;
	
	}
	
	/* simple acessor method */
	protected Date getCheck() {
	
		return check;
	
	}
	
	/* simple acessor method */
	protected void noCheck() {
	
		check = null;
	
	}

	/* simple acessor method */
	protected void newCheck() {
	
		check = new Date();
	
	}
	
	/* simple acessor method */
	protected String getMessage() {
	
		return message;
	
	}

	/* simple acessor method */
	protected char getType() {
	
		return type;
	
	}


	/**
	 * sendTotals creates check date for security, calculates vote counts and sends
	 * on to the Totaller server.
	 *
	 */
	 protected void sendTotals() {	 
				
		Runnable tk;
		Thread talker;

		String reply = "";
		String result = "";
		String session_key, client_hmac_key, client_hmac_seed, message, message_two;
		int vectIndex = 0;
		
		RTServer.carryOn = true;
		check = new Date();
		
		tdCount = 0;

		try {

			RTServer.NORM.info("Calculating and sending totals...");
			
			session_key = RSAEncrypt.makeSessionKey();
			
			client_hmac_seed = RSAEncrypt.makeSessionKey();  // Initialise protocol values
			client_hmac_key = AuthSys.seedToKey(client_hmac_seed);
			
			message = RSAEncrypt.pubEncrypt(RTServer.rt_public_key,session_key) + "-" + RSAEncrypt.blowEncrypt(session_key,"I|" + client_hmac_seed + "|" + AuthSys.makeDigest("I|" + client_hmac_seed));  // send protocol init
			
			/* DO SQL STUFF HERE */
			totalData = DBase.getTotals();
			/* NB Several packets needed for all parties */
			/* when finished we send T|DONE|MAC */
			
			setCheckKey(AuthSys.makeDigest(check.toString()+totalData.elementAt(tdCount)));
			message_two = new Packet('T',checkKey + "-" + check.toString() + "-" + totalData.elementAt(tdCount),session_key,RTServer.rt_public_key,client_hmac_key,2).toCryptString(true); // do first total
			
			if(RTServer.DEV.isDebugEnabled()){
				RTServer.DEV.debug("First total: " + totalData.elementAt(tdCount));
			}
			
			type = 'T';
			
			threadVect.addElement(message_two); // prepare Vector
			vectIndex = threadVect.indexOf(message_two);
			
			setSession(vectIndex, session_key, client_hmac_key, "", client_hmac_seed, "", 0, message_two);
			
			tk = new TCPClient(message, vectIndex);
			talker = new Thread(tk);
			timer = new TimeOutThread();
		
			// insert objects
			threadVect.setElementAt(talker, vectIndex);
			threadTimeOut.insertElementAt(timer, vectIndex);
			
			((Thread) threadTimeOut.elementAt(vectIndex)).start();  // start timeout system
	 		((Thread) threadVect.elementAt(vectIndex)).start();  // start communications

			reply = fromServer;
			
	 	} catch (Exception e) {
	 		RTServer.NORM.error("sendTotal IO Error: " + e.getMessage());
	 	}
	 			
	 }  //EOF sendTotals


	/**
	 * sendKey delivers a key to an ERServer for confirming someone has voted.
	 *
	 * @param akey  The key to send
	 */
	 protected void sendKey(String akey) throws Exception {
		
		Runnable tk;
		Thread talker;
		String reply;
		int vectIndex = 0;
		String session_key, client_hmac_key, client_hmac_seed, message, message_two;
		
		RTServer.carryOn = true;
		type = 'K';

		try {
			session_key = RSAEncrypt.makeSessionKey();
			
			client_hmac_seed = RSAEncrypt.makeSessionKey();  // Initialise protocol values
			client_hmac_key = AuthSys.seedToKey(client_hmac_seed);
			
			message = RSAEncrypt.pubEncrypt(RTServer.er_public_key,session_key) + "-" + RSAEncrypt.blowEncrypt(session_key,"I|" + client_hmac_seed + "|" + AuthSys.makeDigest("I|" + client_hmac_seed));  // send protocol init

			message_two = new Packet('K',akey,session_key,RTServer.er_public_key,client_hmac_key,2).toCryptString(true); // send real data
			
			if(RTServer.DEV.isDebugEnabled()) {
				RTServer.DEV.debug("Sending AuthKey: " + akey);
			}
		
			threadVect.addElement(message_two); // prepare Vector
			vectIndex = threadVect.indexOf(message_two);
			
			setSession(vectIndex, session_key, client_hmac_key, "", client_hmac_seed, "", 0, message_two);
			
			tk = new TCPClient(message, vectIndex);
			talker = new Thread(tk);
			timer = new TimeOutThread();
			
			// insert objects
			threadVect.setElementAt(talker, vectIndex);
			threadTimeOut.insertElementAt(timer, vectIndex);
			
			((Thread) threadTimeOut.elementAt(vectIndex)).start();  // start timeout system
	 		((Thread) threadVect.elementAt(vectIndex)).start();  // start communications

		 	reply = fromServer;
		 	
	 	} catch (Exception e) {
	 		RTServer.NORM.error("sendKey IO Error: " + e.getMessage());
	 	}
	 			
	 }  //EOF sendKey


	/**
	 * getTotalVoted communicates with the ERServer to find out how many people
	 * voted. This can be compared with the number of votes registered to give
	 * an idea of validity.
	 *
	 */
	 protected void getTotalVoted() {

		Runnable tk;
		Thread talker;
		int vectIndex = 0;

		String reply="";
		String session_key, client_hmac_key, client_hmac_seed, message, message_two;
		
		try {
		
			RTServer.carryOn = true;
			type = 'Q';
	
			/* Get verification data from ERServer */
			RTServer.NORM.info("Getting verification data from ERServer...");
			
			session_key = RSAEncrypt.makeSessionKey();
			
			client_hmac_seed = RSAEncrypt.makeSessionKey();  // Initialise protocol values
			client_hmac_key = AuthSys.seedToKey(client_hmac_seed);
			
			message = RSAEncrypt.pubEncrypt(RTServer.er_public_key,session_key) + "-" + RSAEncrypt.blowEncrypt(session_key,"I|" + client_hmac_seed + "|" + AuthSys.makeDigest("I|" + client_hmac_seed));  // send protocol init
			
			message_two = new Packet('Q',"TOTALVOTED",session_key,RTServer.er_public_key,client_hmac_key,2).toCryptString(true); // send real data
		
			if(RTServer.DEV.isDebugEnabled()) {
				RTServer.DEV.debug("Sending: Q|TOTALVOTED");
			}

			tk = new TCPClient(message, vectIndex);
			talker = new Thread(tk);
			timer = new TimeOutThread();
			
			threadVect.addElement(message_two); // prepare Vector
			vectIndex = threadVect.indexOf(message_two);
			
			setSession(vectIndex, session_key, client_hmac_key, "", client_hmac_seed, "", 0, message_two);
			
			//insert objects
			threadVect.setElementAt(talker, vectIndex);
			threadTimeOut.insertElementAt(timer, vectIndex);
			
			((Thread) threadTimeOut.elementAt(vectIndex)).start();  // start timeout system
	 		((Thread) threadVect.elementAt(vectIndex)).start();  // start communications

		 	reply = fromServer;
		 } catch (Exception e) {
		 	RTServer.NORM.error("Verification check error: " + e.getMessage());
		 	RTServer.carryOn = false;
		 }
		 	
	 }  //EOF getTotalVoted


	/**
	 * sendDiag dispatches any diagnostic packets for debugging.
	 *
	 * @param msg  Data for packet
	 */
	 protected void sendDiag(String msg) {	 
		
		Runnable tk;
		Thread talker;

		String reply="";
		int vectIndex = 0;
		String session_key, client_hmac_key, client_hmac_seed, message, message_two;
		
		RTServer.carryOn = true;
		type = 'D';
	
		try {
			session_key = RSAEncrypt.makeSessionKey();
			
			client_hmac_seed = RSAEncrypt.makeSessionKey();  // Initialise protocol values
			client_hmac_key = AuthSys.seedToKey(client_hmac_seed);
			
			message = RSAEncrypt.pubEncrypt(RTServer.er_public_key,session_key) + "-" + RSAEncrypt.blowEncrypt(session_key,"I|" + client_hmac_seed + "|" + AuthSys.makeDigest("I|" + client_hmac_seed));  // send protocol init
			
			message_two = new Packet('D',msg,session_key,RTServer.er_public_key,client_hmac_key,2).toCryptString(true); // send real data
			
			if(RTServer.DEV.isDebugEnabled()) {
				RTServer.DEV.debug("Sending: D|" + msg);
			}
		
			tk = new TCPClient(message, vectIndex);
			talker = new Thread(tk);
			timer = new TimeOutThread();
		
			threadVect.addElement(message_two); // prepare Vector
			vectIndex = threadVect.indexOf(message_two);
			
			setSession(vectIndex, session_key, client_hmac_key, "", client_hmac_seed, "", 0, message_two);
			
			//insert objects
			threadVect.setElementAt(talker, vectIndex);
			threadTimeOut.insertElementAt(timer, vectIndex);
			
			((Thread) threadTimeOut.elementAt(vectIndex)).start();  // start timeout system
	 		((Thread) threadVect.elementAt(vectIndex)).start();  // start communications

		 	reply = fromServer;
		 	
	 	} catch (Exception e) {
	 		RTServer.NORM.error("sendDiag IO Error: " + e.getMessage());
	 	}
	 			
	 }  //EOF sendDiag

	
} //EOF Class