/************************************
*						*
*	Flexible Client Program		*
*						*
*	BY: Simeon Bassett		*
*						*
*	simeon_b@hotmail.com		*
*						*
*	This comment must remain.	*
*						*******************************************
*													*
* 	More information about the program and the 					*
* 	source code can be found within the readme.txt file (if there is one) 	*
*													*
*	You may freely ditribute this as you wish, however you may not alter it	*
*	in any way without my permission.							*
*													*
*	If you do alter it please leave my comments in.					*
*	You MUST leave in this comment.							*
*													*
*******************************************************************************/

import java.io.*;
import sun.net.*;

class rawclient extends Thread
{

	//Globals
	static NetworkClient net;		//used to create the client object and then input and print streams from that
	static BufferedReader net_input;
	static PrintStream net_output;
	static BufferedReader input;
	static String range = "port must be a whole number in the range of 0 - 65536";

	//Here is the string for the help command
	static String help = "More information is in the readme.txt file  \n\n to send information to the server, simply type in the string and hit enter, to get information, hit enter on a blank line   \n\nCommands:  \n help - Displays this message  \n quit - quits the program  \n restart - restarts the program without quitting  \n repeat - repeats a certain message  \n delay repeat - not implemented yet  \n port scan - checks for open ports on a computer  \n port fondle - repeatedly connects to a server  \n view repeat - view the message send in repeat  \n send - send a message to the server, used for sending a piece of information that is already a command such as quit or help \n";

	static boolean noCommand = true;
	static String header = "Raw Client   By: Simeon Bassett    simeon_b@hotmail.com \n";
	static boolean firstCycle = true;
	static String server;
	static int port; 
	static String repeatBuffer = "";		//for the repeat command
	static int delay = 200;

	public static void main(String arg[])
	{
		if (firstCycle) 			//do these only when the program starts
		{
			System.out.println(header);
		
			input = new BufferedReader(new InputStreamReader(System.in));
			
			firstCycle = false;
		}

		try
		{
			System.out.print("server:");
			server = input.readLine();
			if(server.equals(""))
			{
				server = "cjnetworks.com";
				System.out.println(server);
			}
			System.out.print("port:");
			String tempPort = input.readLine();
			if(tempPort.equals(""))
			{
				port = 25;
			}
			else
			{
				port = (new Integer(tempPort)).intValue();
			}

			System.out.println("port has been read (" + port + ")"  );			//debugging info

			if( (check_server(server) && check_port(port)) )
			{
				System.out.println("Creating client object");
				net = new NetworkClient(server, port);
				System.out.println("Client object Created");
			}
			else
			{
				System.out.println("There was an error in the client specification");
				main(null);
				return;
			}

		}
		catch(IOException e)
		{
			System.out.println("connection not possible");
		}
		catch(NumberFormatException e)
		{
			System.out.println(range);
		}
		catch(NullPointerException e)
		{
			System.out.println("There was an error in the client specification");
		}

		if(!net.serverIsOpen())
		{
			System.out.println("server is not open");
			main(null);
			return;
		}

		net_input = new BufferedReader(new InputStreamReader(net.serverInput));
		net_output = net.serverOutput;

		program(input, net_input, net_output);

		return;

	}

/**************************************
*	   Program Core			  *
**************************************/

/*  I didn't name the variables in the program() 
function the same as the globals because 
of scope problems that I didn't want to work around 
with the scope resolution operator. I guess that will teach me to use
global variables.  */

	static void program(BufferedReader program_input, BufferedReader program_net_input, PrintStream program_net_output)
	{	
		String command = "";

		System.out.print("%");
		try { command = program_input.readLine(); } catch(IOException e) { System.out.println(e); return;  }


		/********************
		*	Features	  *
		********************/
		if (command.equalsIgnoreCase("help"))
		{
			System.out.println(help); 
			noCommand = false;
		}
		if (command.equalsIgnoreCase("quit"))
		{
			cleanUp();
			noCommand = false;
			return;
		}
		if (command.equalsIgnoreCase("restart"))
		{
			main(null);
			return;
		}
		if (command.equalsIgnoreCase("repeat"))
		{
			int countMax = 0;
			int count = 0;
			String sync = "0";
			repeatBuffer = "";			

			System.out.print("message: ");
			while(!(repeatBuffer.endsWith("*\n")))
			{
				try { repeatBuffer += program_input.readLine() + "\n"; } catch(IOException e) { }
			}
			
			repeatBuffer = repeatBuffer.substring( 0 , repeatBuffer.length() - 2 ); //shave the ending command off

			System.out.print("Times to repeat: ");
			try
			{
				countMax = (new Integer(program_input.readLine())).intValue();
			}
			catch(IOException e)
			{
				System.out.println("IO error  " + e);
			}
			
			System.out.print("Sync to input from server: ");
			try { sync = program_input.readLine(); } catch(IOException e) { }
			if(sync.equals(""))
			{
				while(count < countMax)
				{
					program_net_output.println(repeatBuffer);
					count++;
				}				
			}
			else
			{
				String netInput = "";
				boolean breakLoop = false;
				
				System.out.println(repeatBuffer);

				while(count < countMax)
				{
					program_net_output.println(repeatBuffer);

					while(true) 	//wait until sync message is encountered before moving on
					{ 	
						try
						{
							if(net_input.ready())
								netInput = net_input.readLine();

							System.out.println(netInput); 

							if ( netInput.lastIndexOf(sync) != -1 )
							{
								breakLoop = true;
							}

							sleep(delay);
						}
						catch(IOException e)
						{  
						}
						catch(InterruptedException e)
						{
						}

						if(breakLoop)
							break;

					} 
 
					count++;
					try { sleep(delay); } catch(InterruptedException e) {  }
				}
			}

			command = "";
		}
		if (command.equalsIgnoreCase("delay repeat"))
		{
			
		}
		if (command.equalsIgnoreCase("port scan"))
		{
			//Create the variables
			String portScanServer = "";			
			int portsFound = 0;
			int startAt = 0;	
			int endAt = 65536;
			int threads = 1;
			int timeout = 1000;
			int split;
			int portPoint;
			int extraPorts = 0;
			int totalScanningPorts;
			
			//Reset portScan's static variables
			portScan.totalPortsFound = 0;
			portScan.finishedThreads = 0;
			portScan.portsChecked = 0;

			//Get user Input
			try 
			{
				//lots of user input for this one
				System.out.print("Server: ");
				portScanServer = program_input.readLine(); 

				System.out.print("Start at: ");
				startAt = (new Integer(program_input.readLine())).intValue();

				System.out.print("End at: ");
				endAt = (new Integer(program_input.readLine())).intValue();

				System.out.print("Threads: ");
				threads = (new Integer(program_input.readLine())).intValue();

				/* System.out.print("timeout: ");
				timeout = (new Integer(program_input.readLine())).intValue();  */

				System.out.print("Open ports found at: ");
			} 
			catch(IOException e) 
			{  
			}
			
			//Variable Arithmetic to set up for port scan
			totalScanningPorts = endAt - startAt;

			//if there are more thread than ports just go with one thread to each port
			if (totalScanningPorts < threads)
				threads = totalScanningPorts;

			extraPorts = totalScanningPorts % threads;
			split = (totalScanningPorts - extraPorts)/threads;
			portPoint = startAt + split + extraPorts;
			
			portScan[] scanner = new portScan[threads];

			scanner[0] = new portScan(portScanServer , startAt , portPoint , timeout );
			scanner[0].start();

			portPoint++;

			int count = 1;
			while (count < threads)
			{
				scanner[count] = new portScan(portScanServer , portPoint , portPoint + split , timeout);
				scanner[count].start();
				portPoint += split + 1;
				count++;
			}	
			
			while(portScan.finishedThreads < threads)	//check to make sure that all of the threads have finished and wait until they do
			{	
				try
				{
					sleep(1000);
				}
				catch(InterruptedException e)
				{
				}
			}
			
			System.out.print("\n");
			System.out.println(portScan.portsChecked + " ports were checked");

			if(portScan.totalPortsFound == 0)
				System.out.print("there were no open ports found, how dissappointing!");
			else
				System.out.print("there were " + portScan.totalPortsFound + " open ports found ");

			System.out.print("\n");
			noCommand = false;
		}
		if (command.equalsIgnoreCase("port fondle"))
		{
			String server;
			int port;
			int cycles;
			int countInner = 0;
			int countOuter = 0;
			int connectDelay = 0;

			try
			{	
				//the input
				System.out.print("Server: ");
				server = program_input.readLine();

				System.out.print("port: ");
				port = (new Integer(program_input.readLine())).intValue();

				System.out.print("cycles: ");
				cycles = (new Integer(program_input.readLine())).intValue();

				System.out.print("delay: ");
				connectDelay = (new Integer(program_input.readLine())).intValue();
				
				//The meat
				while(countOuter < cycles)
				{
					while ( countInner < 65536 ) 
					{
						try
						{
							new java.net.Socket(server , port);
							sleep(connectDelay);
							countInner++;
						}
						catch(IOException e)
						{
						}
						catch(InterruptedException e)
						{
						}
					}

					countOuter++;
					System.out.print(".");
				}

				System.out.println("All cycles complete");
			}
			catch(IOException e)
			{
				System.out.print(e);
			}

			noCommand = false;
		}
		if (command.equalsIgnoreCase("view repeat"))
		{
			System.out.println(repeatBuffer);
			noCommand = false;
		}
		if (command.equalsIgnoreCase("send"))
		{
			System.out.print("-->");
			try { command = program_input.readLine(); } catch(IOException e) { }
		}
		if (noCommand)
		{
			if(!(command.equals("")))
				program_net_output.println(command);
			else
				printNet();
			
		}
		/******************
		*   End Features	*
		******************/

		program(program_input, program_net_input, program_net_output);
		
		return;
	}


/**************************************
*	        Functions			  *
**************************************/

	static void cleanUp()
	{
		printNet();

		//Clean up time
		try
		{	
			//close the global client object
			net.closeServer();

			//close the global IO streams
			net_input.close();
			net_output.close();
		}
		catch(Exception e)
		{
			System.out.println(e);
		}

	}

	static void printNet()
	{
		String netInput = "";
		
		try 
		{ 
			while(net_input.ready()) 
			{ 
				netInput = net_input.readLine();
				System.out.println(netInput); 
			}  
		} 
		catch(IOException e) 
		{   
			System.out.println("could not get input from server");
		}
		
		return;
	}

	static boolean check_server(String server)
	{
		if(server.equals(""))
		{
			System.out.println("server not valid");
			return false;
		}
		return true;
	}

	static boolean check_port(int port)
	{

		if(port > 65536)
		{
			System.out.println(range + " (more than 65536)");
			return false;
		}

		if(port < 0)
		{
			System.out.println(range + " (less than 0)");
			return false;
		}

		return true;
	}

}


/******************************
*	Classes			*
******************************/

class portScan extends Thread
{	

	java.net.Socket server;
	String serverName;	
	int startingPort;
	int endingPort;
	int count;
	int timeOut;

	static final int portMin = 0;
	static final int portMax = 65536;

	public static int totalPortsFound = 0;
	public static int finishedThreads = 0;
	public static int portsChecked = 0;

	public portScan(String serverAddress, int startPort, int endPort , int timeout)
	{
			serverName = serverAddress;
			count = startingPort = startPort;
			endingPort = endPort;
			timeOut = timeout;
	}

	public void run()
	{
		while(count <= endingPort)
		{	
			try
			{
				server = new java.net.Socket(serverName , count);
				System.out.print(" " + count + " ");
				portScan.totalPortsFound++;
			}
			catch(IOException e)
			{
				System.out.print(".");
				portsChecked++;
			}
			count++;
		}
		
		finishedThreads++;
		return;
	}

}