import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.GregorianCalendar; import java.util.Scanner; import java.util.TimeZone; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; /** * @author Joshua Wilkins * @version 1.05 - last updated 5/2/2008 * Client class for the Clock in and out program, connects to the server and allows the sending of commands to the server. * built for Networks First Semester Final Project, a graduate level course (502). * Environment variables for system or IDE must have the mysql-connector-java-5.1.5-bin.jar set up for the system to work, * as well as a working mysql server. The server class contains the code for interaction with the mysql server. */ public class Client { private Scanner in; private Scanner SocketIn; private PrintWriter SocketOut; private PrintWriter out; private FileWriter fileOut; private File file; private Socket socket; private int errorCount; private State presentState; private int eventState = 0; private Event eventType; private boolean isUsrEvent; private boolean isTimerEvent; private boolean isServerEvent; private int[][] eventStateTable; private GregorianCalendar calendar; private String defaultServerAddress = "localhost"; private int defaultServerPort = 19000; private String frame; private boolean programStarted, encryptionMode; private String serverInput, userInput; private ArrayList<String> serverInputList; private boolean timeToListenToServer = false; private byte[] keyBytes = new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0x00}; private SecretKey key = new SecretKeySpec(keyBytes,"DES");; private DesEncrypter encrypter = new DesEncrypter(key); /** * State machine event values */ private enum Event { IOREQ, CNCTREQ, RJCTRCVD, TEXP, CLOSEREQ, USRDATAREQ, CLOSEACKRCVD, ACCPTRECVD, IORESPNSERCVD, DATARESPNSERCVD } /** * State machine state values */ private enum State { UNCONNECTED, PENDING_CONNECTION, CONNECTED, WAIT_IO_CONFIRMATION, WAIT_DATA_RESPNSE, PENDING_CLOSE } /** * Default constructor */ public Client() { initialize(); loop(); } /** * Constructor with added user parameters */ public Client(String serverAddress, int serverPort) { defaultServerAddress = serverAddress; defaultServerPort = serverPort; Client client = new Client(); } /** * Constructor with single user parameter */ public Client(String serverAddress) { defaultServerAddress = serverAddress; Client client = new Client(); } public static void main(String[] args) { Client client; if (args != null && args.length > 0) { if (args.length == 2) { client = new Client(args[0], Integer.parseInt(args[1])); } client = new Client(args[0]); } else { client = new Client(); } } /** * initializes the client and statemachine */ private void initialize() { System.out.println("Program Starting!"); System.out.println("Type COMMANDS for a list of commands."); presentState = State.UNCONNECTED; errorCount = 0; eventType = Event.CNCTREQ; programStarted = true; isUsrEvent = false; isTimerEvent = false; isServerEvent = false; encryptionMode = false; serverInputList = new ArrayList<String>(); calendar = new GregorianCalendar(TimeZone.getDefault()); buildEventStateTable(); in = new Scanner(System.in); } /** * Basic state machine event loop */ private void loop() { while (true) { int eventState = 0; if (programStarted) { System.out .println("Program Initialized. Attempting to connect to server..."); eventState = findEventState(eventType, presentState); caseSwitch(eventState); programStarted = false; } if (programStarted == false) { if (timeToListenToServer) { try { if (SocketIn.hasNextLine()) { serverInput = SocketIn.nextLine(); catchServerEvent(serverInput); if (isServerEvent) { eventState = findEventState(eventType, presentState); caseSwitch(eventState); } } else { System.out.println("we skipped the socket in :("); } } catch (Exception e) { // do nothing if socket hasn't been connected; } } if (in.hasNextLine()) { userInput = in.nextLine(); if (userInput.equals("COMMANDS")) { System.out .println("USER COMMANDS (CASE-SENSATIVE): IOREQ, CNCTREQ, CLOSEREQ, USRDATAREQ, QUIT"); timeToListenToServer = false; } if (userInput.equals("QUIT")) { timeToListenToServer = false; if (presentState.equals(State.UNCONNECTED)) { System.out.println("Quitting..."); break; } else { timeToListenToServer = false; System.out .println("You forgot to close the connection with the server!"); System.out .println("Please use the command: CLOSEREQ before quitting."); } } catchUsrEvent(userInput); if (isUsrEvent) { eventState = findEventState(eventType, presentState); caseSwitch(eventState); } } } } } /** * State machine for the client */ @SuppressWarnings("static-access") private void caseSwitch(int eventState) { switch (eventState) { case 0: { // Nothing happens case System.out.println("and nothing happens..."); break; } case 1: { // IOREQ && UNCONNECTED encryptionMode = false; timeToListenToServer = false; System.out .println("Client not currently connected to server..."); System.out.println("...Writing to log file..."); System.out.println("Please enter your user ID..."); String userID = in.nextLine(); try { fileOut = new FileWriter("log.txt", true); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } out = new PrintWriter(fileOut, true); out.print(userID); System.out.println("Please enter your password..."); String userPass = in.nextLine(); out.print(" " + userPass); out.print(" IN/OUT"); calendar = new GregorianCalendar(TimeZone.getDefault()); int temp = calendar.get(calendar.MONTH); out.print(" " + (temp + 1) + "/" + calendar.get(calendar.DAY_OF_MONTH) + "/" + calendar.get(calendar.YEAR) + " " + calendar.get(calendar.HOUR_OF_DAY) + ":" + calendar.get(calendar.MINUTE) + ":" + calendar.get(calendar.SECOND)); out.println(); System.out.println("writing to file..."); try { Thread.sleep(800); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("...done"); out.close(); presentState = State.UNCONNECTED; eventType = null; break; } case 2: { // IOREQ && CONNECTED encryptionMode = true; System.out.println("Please Enter Your UserID..."); String userID = in.nextLine(); String encUserID = encData(userID); System.out.println("Please enter your password..."); String password = in.nextLine(); String encPassword = encData(password); System.out.println("Transmitting..."); frame = encData("IOREQ"); txFrame(frame); frame = encUserID; txFrame(frame); frame = encPassword; txFrame(frame); presentState = State.WAIT_IO_CONFIRMATION; timeToListenToServer = true; eventType = null; break; } case 3: { // CONNECT REQ & UNCONNECTED encryptionMode = false; try { socket = new Socket(defaultServerAddress, defaultServerPort); SocketIn = new Scanner(socket.getInputStream()); SocketOut = new PrintWriter(socket.getOutputStream(), true); } catch (Exception e) { // TODO Auto-generated catch block // e.printStackTrace(); System.out .println("Socket Connection Failed! Perhaps the server is not online or incorrect hostname & port entered?"); System.out.print("Aborting"); try { Thread.sleep(800); System.out.print("."); Thread.sleep(800); System.out.print("."); Thread.sleep(800); System.out.print(".\n"); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } presentState = State.UNCONNECTED; eventType = null; break; } String str = "CONNECT"; frame = encData(str); txFrame(frame); // start_timer; timeToListenToServer = true; presentState = State.PENDING_CONNECTION; eventType = null; break; } case 4: { // RJCTRCVD && PENDING CONNECTION encryptionMode = false; System.out .println("Failed to connect Securely to server, Reject Message Received."); System.out .println("Perhaps the Private key for encryption needs updating on this machine?"); presentState = State.UNCONNECTED; eventType = null; break; } // I decided against implementing time-outs given the time // allotment for the project case 5: { // TEXP & PENDING CONNECTION break; } case 6: { // TEXP & WaitIOConf break; } case 7: { // TEXP & WaitDataRspns break; } case 8: { // TEXP & Pending CLose break; } case 9: { // CloseREQ & Connected encryptionMode = true; String str = "CLOSEREQ"; frame = encData(str); txFrame(frame); timeToListenToServer = true; presentState = State.PENDING_CLOSE; eventType = null; break; } case 10: { // USRData REQ and Unconnected encryptionMode = false; timeToListenToServer = false; System.out .println("Not connected to server, cant retreive database data...aborting..."); presentState = State.UNCONNECTED; eventType = null; break; } case 11: { // UsrDataReq and Connected boolean temp = true; boolean temp2 = true; boolean all = false; boolean single = false; encryptionMode = true; System.out .println("Is this a Single Employee hours request or All currently clocked in employees? Specify either SINGLE or ALLCLOCKEDIN"); while (temp2) { String userInput = in.nextLine(); if (userInput.equals("SINGLE") || userInput.equals("ALLCLOCKEDIN")) { if(userInput.equals("ALLCLOCKEDIN")) { all = true; } if(userInput.equals("SINGLE")) { single = true; } frame = encData(userInput); txFrame(frame); temp2 = false; } else { System.out .println("Incorrect Command entered, please try again."); } } if(single) { System.out .println("Enter employee's username"); String username = in.nextLine(); System.out.println("Enter start date in the format mm-dd-yyyy"); String startDate = in.nextLine(); System.out.println("Enter end date in the format mm-dd-yyyy"); String endDate = in.nextLine(); frame = encData(username); txFrame(frame); frame = encData(startDate); txFrame(frame); frame = encData(endDate); txFrame(frame); } else { //nothin } presentState = State.WAIT_DATA_RESPNSE; eventType = null; timeToListenToServer = true; break; } case 12: { // CloseAck Recieved && Pending Close encryptionMode = false; System.out.println("Connection Closed Successfully..."); presentState = State.UNCONNECTED; timeToListenToServer = false; eventType = null; break; } case 13: { // Accpt Recieved && Pending Connection System.out.println("Connection sucessful..."); encryptionMode = true; presentState = State.CONNECTED; timeToListenToServer = false; eventType = null; break; } case 14: { // IO Response Recieved && WaitIOConf encryptionMode = false; String ioResponse = SocketIn.nextLine(); String decIOresponse = decData(ioResponse); //System.out.println(decIOresponse); if (decIOresponse.startsWith("IN")) { System.out.println("Clock-IN OKAY: " + decIOresponse); } else if (decIOresponse.startsWith("OUT")) { System.out.println("Clock-OUT OKAY: " + decIOresponse); } else { System.out.println(decIOresponse); } presentState = State.CONNECTED; timeToListenToServer = false; eventType = null; break; } case 15: { // DataRSPNS Received && WaitDataResponse encryptionMode = false; boolean temp = true; while (temp) { String dataResponse = SocketIn.nextLine(); String decDataResponse = decData(dataResponse); if (decDataResponse.equals("END")) { temp = false; } else { System.out.println(decDataResponse); } } presentState = State.CONNECTED; timeToListenToServer = false; eventType = null; break; } } } /** * Builds the event State table for use by the State Machine */ private void buildEventStateTable() { eventStateTable = new int[10][6]; for (int r = 0; r < eventStateTable.length; r++) { for (int c = 0; c < eventStateTable[r].length; c++) { if (r == 0 && c == 0) { eventStateTable[r][c] = 1; } else if (r == 0 && c == 2) { eventStateTable[r][c] = 2; } else if (r == 1 && c == 0) { eventStateTable[r][c] = 3; } else if (r == 2 && c == 1) { eventStateTable[r][c] = 4; } else if (r == 3 && c == 1) { eventStateTable[r][c] = 5; } else if (r == 3 && c == 3) { eventStateTable[r][c] = 6; } else if (r == 3 && c == 4) { eventStateTable[r][c] = 7; } else if (r == 3 && c == 5) { eventStateTable[r][c] = 8; } else if (r == 4 && c == 2) { eventStateTable[r][c] = 9; } else if (r == 5 && c == 0) { eventStateTable[r][c] = 10; } else if (r == 5 && c == 2) { eventStateTable[r][c] = 11; } else if (r == 6 && c == 5) { eventStateTable[r][c] = 12; } else if (r == 7 && c == 1) { eventStateTable[r][c] = 13; } else if (r == 8 && c == 3) { eventStateTable[r][c] = 14; } else if (r == 9 && c == 4) { eventStateTable[r][c] = 15; } else { eventStateTable[r][c] = 0; } } } } /** * Catches events passed from the server and assigns the correct statemachine values for that event */ private void catchServerEvent(String input) { String decodedInput; if (encryptionMode) { decodedInput = decData(input); if (checkServerEvent(decodedInput)) { if (decodedInput.equals("REJECTED")) { eventType = Event.RJCTRCVD; isServerEvent = true; } if (decodedInput.equals("CLOSEACK")) { eventType = Event.CLOSEACKRCVD; isServerEvent = true; } if (decodedInput.equals("ACCEPT")) { eventType = Event.ACCPTRECVD; isServerEvent = true; } if (decodedInput.equals("IORESPONSE")) { eventType = Event.IORESPNSERCVD; isServerEvent = true; } if (decodedInput.equals("DATARESPONSE")) { eventType = Event.DATARESPNSERCVD; isServerEvent = true; } } } else { if (checkServerEvent(input)) { if (input.equals("REJECTED")) { eventType = Event.RJCTRCVD; isServerEvent = true; } if (input.equals("CLOSEACK")) { eventType = Event.CLOSEACKRCVD; isServerEvent = true; } if (input.equals("ACCEPT")) { eventType = Event.ACCPTRECVD; isServerEvent = true; } if (input.equals("IORESPONSE")) { eventType = Event.IORESPNSERCVD; isServerEvent = true; } if (input.equals("DATARESPONSE")) { eventType = Event.DATARESPNSERCVD; isServerEvent = true; } } } } /** * Decodes the encrypted string */ private String decData(String input) { String output = encrypter.decrypt(input); return output; } /** * checks to makesure the server event is a valid event */ private boolean checkServerEvent(String input) { boolean temp = false; String str[] = { "REJECTED", "CLOSEACK", "ACCEPT", "IORESPONSE", "DATARESPONSE" }; for (String str1 : str) { if (input.equals(str1)) { temp = true; } } return temp; } /** * Checks to make sure the user event is valid */ private void catchUsrEvent(String input) { if (checkInput(input)) { for (Event event : Event.values()) { if (input.equals(event.toString())) { eventType = event; isUsrEvent = true; } } } else { isUsrEvent = false; } } /** * finds the eventStateTable integer value for the given event and state */ private int findEventState(Event event, State currentState) { int eventState = 0; int eventInt = findEventInt(event); int stateInt = findStateInt(currentState); for (int r = 0; r < eventStateTable.length; r++) { for (int c = 0; c < eventStateTable[r].length; c++) { if (eventInt != 42 || stateInt != 42) { eventState = eventStateTable[eventInt][stateInt]; } } } return eventState; } /** * Helper method for the findEventState method */ private int findStateInt(State state) { int stateInt = 42; if (state.equals(State.UNCONNECTED)) { stateInt = 0; } else if (state.equals(State.PENDING_CONNECTION)) { stateInt = 1; } else if (state.equals(State.CONNECTED)) { stateInt = 2; } else if (state.equals(State.WAIT_IO_CONFIRMATION)) { stateInt = 3; } else if (state.equals(State.WAIT_DATA_RESPNSE)) { stateInt = 4; } else if (state.equals(State.PENDING_CLOSE)) { stateInt = 5; } else { System.out.println("Invalid State Error"); } return stateInt; } /** * Helper method for the findEventState method */ private int findEventInt(Event event) { int eventInt = 42; if (event.equals(Event.IOREQ)) { eventInt = 0; } else if (event.equals(Event.CNCTREQ)) { eventInt = 1; } else if (event.equals(Event.RJCTRCVD)) { eventInt = 2; } else if (event.equals(Event.TEXP)) { eventInt = 3; } else if (event.equals(Event.CLOSEREQ)) { eventInt = 4; } else if (event.equals(Event.USRDATAREQ)) { eventInt = 5; } else if (event.equals(Event.CLOSEACKRCVD)) { eventInt = 6; } else if (event.equals(Event.ACCPTRECVD)) { eventInt = 7; } else if (event.equals(Event.IORESPNSERCVD)) { eventInt = 8; } else if (event.equals(Event.DATARESPNSERCVD)) { eventInt = 9; } else { System.out.println("invalid event error"); } return eventInt; } /** * Checks the user input for correct syntax */ private boolean checkInput(String input) { boolean temp = false; String[] str = { "IOREQ", "CNCTREQ", "CLOSEREQ", "USRDATAREQ" }; for (String str1 : str) { if (input.compareTo(str1) == 0) { temp = true; } } if (temp == false) { if (!input.equals("COMMANDS")) System.out.println("Invalid Command or Syntax"); } return temp; } /** * encodes the data to be sent */ private String encData(String str) { String encStr = encrypter.encrypt(str); return encStr; } /** * transmits the frame */ private void txFrame(String str) { try { SocketOut.println(str); } catch (Exception e) { System.err .println("NOT CONNECTED! Cannot Transmit Frame!"); } } }