package mykeynote.server.persistent;
import java.net.Socket;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.File;
import mykeynote.exceptions.keynote.RetValStringNotParsableException;
import mykeynote.exceptions.keynote.cl.KeyNoteCLException;
import mykeynote.exceptions.keynote.cl.KeyNoteCLProcessCreationException;
import mykeynote.keynote.KeyNoteCL;
import mykeynote.misc.FormatConstants;
import mykeynote.misc.ProtocolConstants;
import mykeynote.server.Main;
import mykeynote.server.Report;
import mykeynote.server.configuration.Configuration;
public class MKNPProtocol extends Thread {
//Global options
private static final String END = ProtocolConstants.END;
private static final String LET = ProtocolConstants.LET;
//Modes
private static final String persistent = ProtocolConstants.PERSISTENTMODE;
//Submodes of persistent
private static final String continuous = ProtocolConstants.CONTINUOUS;
private static final String statMode = ProtocolConstants.STATMODE;
//Environment strings
private static final String resource = ProtocolConstants.RESOURCE;
private static final String endResource = ProtocolConstants.ENDRESOURCE;
private static final String key = ProtocolConstants.KEY;
private static final String endKey = ProtocolConstants.ENDKEY;
private static final String cred = ProtocolConstants.CRED;
private static final String endCred = ProtocolConstants.ENDCRED;
private static final String env = ProtocolConstants.ENV;
private static final String endEnv = ProtocolConstants.ENDENV;
private static final String currentVersion = "0.3";
//Report & config
private Report report;
private Configuration config;
// Non static variables
private Socket socket = null;
private String clientString;
private String unique;
private BufferedReader in;
private PrintWriter out;
private BufferedWriter bw = null;
private String input; //We read each line with this command
private int credFrom;
private int credTo;
@SuppressWarnings("unused")
private String clientVersion = null;
private boolean persistentBool = false; //is the connection persistent
private File directory;
private String resourceSearched;
//if a client invokes an error, than the client should be disconnected
//if the error is because of the server, than the client should not be disconnected
//true means allow the error and continue, false = stop
private boolean allowError = false;
public MKNPProtocol(Socket socket, String clientString, Configuration config, Report report, String unique) {
this.config = config;
this.report = report;
this.socket = socket;
this.clientString = clientString;
this.unique = unique;
try {
this.out = new PrintWriter(socket.getOutputStream());
} catch (IOException e) {
report.reportErrorLog(clientString, "Unable to open output stream!");
return;
}
try {
this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()), 1024);
} catch (IOException e) {
report.reportErrorLog(clientString, "Unable to open input stream!");
return;
}
}
public MKNPProtocol(BufferedReader in, PrintWriter out, String clientString, Configuration config, Report report, String unique){
this.config = config;
this.report = report;
this.in = in;
this.out = out;
this.clientString = clientString;
this.unique = unique;
}
public void run(){
chooseMode(false);
}
private void chooseMode(boolean wasRead){
versions(wasRead);
//choosing persistent mode
if(input.compareTo(persistent) == 0){
if(config.getUsePServer()){
persistent();
} else {
out.println("false\nThe Server does not allow any persistent connections!");
out.flush();
}
} else {
// choosing a non persistent mode
if(!config.getUseServer()) {
out.println("false\nThe Server does not allow any non-persistent connections!");
out.flush();
} else {
out.println("false\nUnknown mode " + input +". Please check your client!");
out.flush();
}
}
//Closing
close(persistentBool);
}
private void close(boolean persistent){
// decreasing the active number of connections
if(persistent){
config.decreaseCurrentlyConnectedPC();
} else {
config.decreaseCurrentlyConnectedClients();
}
//signaling the client to end
out.println(END);
out.flush();
//closing the used resources
try {
in.close();
} catch (IOException e) {
// Nothing to be done
}
out.flush();
out.close();
if(socket != null){
try {
socket.close();
} catch (IOException e) {
report.reportErrorLog("server", "Unable to close socket!");
}
}
}
private boolean readLine(){
report.reportDebugLog(unique, input);
try{
input = in.readLine();
if(isInterrupted()){
out.println("false\nStopping server");
out.flush();
return false;
}
} catch (IOException e){
report.reportDebugLog("mknpp", "Unable to read a line " + e.getMessage());
out.println("false");
out.flush();
try {
in.readLine();// we don't care what we recieve
} catch (IOException e1) {
//and thus we ignore this error
}
out.println("IO Exception hit!");
out.flush();
return false;
}
report.reportDebugLog(unique, input);
if(input.compareTo(END) == 0)
return false;
return true;
}
private boolean persistent(){
persistentBool = true; // we are running a persistent connection
while(true){
if(!Main.getStarted()){
out.println("false\nServer shutting down, please try again later.");
out.flush();
return true;
}
if(input.compareTo(continuous) == 0){
if(!continuous()){
if(!allowError){
//we have an error that is not acceptable, stopping the connection
report.reportErrorLog(clientString, "Stopping connection to the client because of a client side error!");
config.increaseNoSuccess();
return false; // than is an error!
}
//error allowed, setting allowError to the default value
report.reportDebugLog(unique, "Error allowed, resuming!");
allowError = false;
}
} if(input.compareTo(statMode) == 0){
out.println("<stat>");
out.println("Stats\n\tSuccess:\t" + config.getSuccess() + "\n\tNo success:\t\t" + config.getNoSuccess() + "\n" + config.getEnviroment() + "\n<stat>");
} if(input.compareTo(END) == 0){
//happy end
return true;
} else {
out.println("false\nUnknown mode " + input +". Please check your client!");
report.reportErrorLog(unique, "The client is using an unknown mode " + input);
out.flush();
config.increaseNoSuccess();
return false;
}
}
}
private boolean continuous(){
if(!readLine())
return false;
//we are starting a new continuous session
if(input.compareTo(key) == 0){
//setting the start to 0
credFrom = 0; credFrom = 0; credTo = 0;
createUnique();
directory = new File(config.getTempDir(), unique);
//basically we get everything, if a "get" has encountered an error/exception,
//than the mode is being broken and the method return false
if(!getKey(true))
return false;
if(getCred(true))
return false;
if(getEnv(true))
return false;
if(getResource(true))
return false;
KeyNoteCL cl = new KeyNoteCL(config, report);
try {
//knt.verify(unique, resourceSearched, credFrom, credTo);
//TODO the resourceSearched File should be some other...
//TODO the key shoule point to the real file
cl.verify(unique, report, new File(resourceSearched), new File(key));
} catch (InterruptedException e) {
allowError = false;
return false;
} catch (IOException e) {
return false;
} catch (KeyNoteCLProcessCreationException e) {
allowError = true;
return false;
} catch (KeyNoteCLException e) {
allowError = true;
return false;
} catch (RetValStringNotParsableException e) {
allowError = true;
return false;
}
return true;
}
//we are continuing a session
//TODO continuous session!
getCredFrom(new File(new File(config.getTempDir(), "querry"), unique).getAbsolutePath());
return true;
}
/**
* This method is used to take a key
* @param wasRead <b>true</b> means that there is a line, that already has beein read.<br>
* <b>false</b> means, that the we need to read a line and than go forword
* @return By success returns <b>true</b>, else <b>false</b>.
*/
private boolean getKey(boolean wasRead){
if(!wasRead){
if(!readLine())
return false;
}
if(input.compareTo(key) == 0){
if(!readLine())
return false;
if(!input.startsWith("\""))
//if it does not start with ", then it does not end with one, have to add them
input = "\"" + input + "\"";
String name = directory + unique + FormatConstants.KEYFORMAT;
bw = makeFile(name);
try {
bw.write(input);
bw.write(FormatConstants.NEWLINE);
} catch (IOException e) {
out.println("false\nIOException while saving key");
out.flush();
report.reportErrorLog(unique, "IOException while saving the key!");
try {
bw.close();
} catch (IOException e1) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving key");
out.flush();
report.reportErrorLog(unique, "IOException while closing the key file!");
return false;
}
if(!readLine())
return false;
if(input.compareTo(endKey) != 0){
out.println("false\nKey can be only one line long");
out.flush();
report.reportErrorLog(unique, "Trying to send a key larger then one line");
return false;
}
//everything is ok, give the LET signal
out.println(LET);
out.flush();
if(!readLine())
return false;
return true;
}
//we are allowed to have no key!
//no LET needs to be given
return true;
}
/* private boolean getKey(boolean wasRead){
if(!wasRead){
if(!readLine())
return false;
}
//all clients need to give key!
if(input.compareTo(key) != 0){
Report.reportDebugLog(unique, "Unable to get the key!");
out.println("false\nNo Key given!");
out.flush();
return false;
}
//only 1 key is allowed!
String name = Main.tempDir + Main.seperator + unique + ".key";
file = new File(name);
hasKey = true; //cool we have a key!
if(file.exists()){
out.println("false\nIOException while creating the key file!");
out.flush();
Report.reportErrorLog(unique, "Key file " + unique + ".key exists!");
return false;
}
try {
bw = new BufferedWriter(new FileWriter(name));
} catch (IOException e) {
out.println("false\nIOException while creating the key file!");
out.flush();
Report.reportErrorLog(unique, "IOException while creating the key file " + unique + ".key.");
return false;
}
//null version clients need to get a LET for every line sent...
if(clientVersion.compareTo(nullVersion) == 0){
out.println(LET);
out.flush();
}
if(!readLine())
return false;
try {
//starting quotes
bw.write("\"");
bw.write(input);
//trailing quotes and newline
bw.write("\"\n");
} catch (IOException e) {
out.println("false\nIOException while writing the key file!");
out.flush();
Report.reportErrorLog(unique, "IOException while writing the key file");
return false;
}
try {
bw.flush();
} catch (IOException e) {
out.println("false\nIOException while saving the key file!");
out.flush();
Report.reportErrorLog(unique, "IOException while saving the key file");
return false;
}
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while closing the key file!");
out.flush();
Report.reportErrorLog(unique, "IOException while closing the key file");
return false;
}
if(clientVersion.compareTo(nullVersion) == 0){
out.println(LET);
out.flush();
}
if(!readLine())
return false;
if(input.compareTo(endKey) != 0){
out.println("false\nThe key should be only 1 line long!");
out.flush();
Report.reportErrorLog(unique, "Key is more then 1 line longer!");
return false;
}
//This is used to make all get methods work alike, because other get methods read 1 line before actually ending
if(clientVersion.compareTo(nullVersion) == 0){
out.println(LET);
out.flush();
}
if(!readLine())
return false;
return true;
}
*/
private boolean getCred(boolean wasRead){
if(!wasRead){
if(!readLine())
return false;
}
if(input.compareTo(cred) != 0)
return true;
while(input.compareTo(cred) == 0){
if(input.compareTo(endCred) == 0){
out.println("false\nThe credential file cannot be empty, please check client!");
out.flush();
report.reportErrorLog(unique, "Sending an empty credential file!");
return false;
}
String name = directory + unique + "_" + credTo + FormatConstants.CREDFORMAT;
credTo++;
bw = makeFile(name);
//we have created the file, we know that now a cred will come, lets read it and save it!
while(input.compareTo(endCred) != 0){
try {
bw.write(input);
bw.write(FormatConstants.NEWLINE);
} catch (IOException e) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportErrorLog(unique, "IOException while writing file " + unique + "_" + credTo + FormatConstants.CREDFORMAT);
try {
bw.close();
} catch (IOException e1) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
if(!readLine()){
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
}
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
out.println(LET);
out.flush();
if(!readLine())
return false;
}
//if it does not start with cred, then it was not a credential, still allowed
return true;
}
/*private boolean getCred(boolean wasRead){
if(!wasRead){
if(!readLine())
return false;
if(clientVersion.compareTo(nullVersion) == 0){
out.println(LET);
out.flush();
}
}
//==========
//NULL VERSION CLIENT (look down for non NV)
//null version has to has a cred, even if empty!
if(clientVersion.compareTo(nullVersion) == 0){
if(input.compareTo(cred) != 0){
out.println("false\nNo credential given, please check client!");
out.flush();
Report.reportErrorLog(unique, "Using null version client with no credential!");
return false;
}
out.println(LET);
out.flush();
if(!readLine())
return false;
//empty cred allowed, but not being saved
if(input.compareTo(endCred) == 0){
out.println(LET);
out.flush();
return true;
}
String name = Main.tempDir + Main.seperator + unique + "_" + credTo + ".cred";
file = new File(name);
if(file.exists()){
out.println("false\nIOException while creating the credential file!");
out.flush();
Report.reportErrorLog(unique, "Credential file " + unique + "_" + credTo + ".cred exists!");
return false;
}
try {
bw = new BufferedWriter(new FileWriter(name));
} catch (IOException e) {
out.println("false\nIOException while creating the credential file!");
out.flush();
Report.reportErrorLog(unique, "IOException while creating the credential file " + unique + "_" + credTo + ".cred");
return false;
}
while(input.compareTo(endCred) != 0){
try {
bw.write(input);
} catch (IOException e) {
Report.reportErrorLog(unique, "IOException while writing file " + unique + "_" + credTo + ".cred");
}
if(clientVersion.compareTo(nullVersion) == 0){
out.println(LET);
out.flush();
}
if(!readLine())
return false;
}
return true;
}
//============
//if you want to implement a full procedure for a specific version
//like for nullversion, please do so here
//============
//for every other version here
while(input.compareTo(cred) == 0){
if(input.compareTo(endCred) == 0){
out.println("false\nThe credential file cannot be empty, please check client!");
out.flush();
Report.reportErrorLog(unique, "Sending an empty credential file!");
return false;
}
credTo++;
String name = Main.tempDir + Main.seperator + unique + "_" + credTo + ".cred";
file = new File(name);
if(file.exists()){
out.println("false\nIOException while creating the credential file!");
out.flush();
Report.reportErrorLog(unique, "Credential file " + unique + "_" + credTo + ".cred exists!");
return false;
}
try {
bw = new BufferedWriter(new FileWriter(name));
} catch (IOException e) {
out.println("false\nIOException while creating the credential file!");
out.flush();
Report.reportErrorLog(unique, "IOException while creating the key file " + unique + "_" + credTo + ".cred");
return false;
}
//we have created the file, we know that now a cred will come, lets read it and save it!
while(input.compareTo(endCred) != 0){
try {
bw.write(input);
} catch (IOException e) {
Report.reportErrorLog(unique, "IOException while writing file " + unique + "_" + credTo + ".cred");
}
if(!readLine())
return false;
}
return true;
}
//if it does not start with cred, then it was not a credential, still allowed
return true;
}*/
private boolean getEnv(boolean wasRead){
if(!wasRead){
if(!readLine())
return false;
}
//es muss ein enviroment gegeben werden
if(input.compareTo(env) != 0){
out.println("false\nNo enviroment given!");
report.reportErrorLog(unique, "No enviroent given using null version cleint!");
return false;
}
if(!readLine())
return false;
if(input.compareTo(endEnv) == 0){
out.println("false\nThe enviroment file cannot be empty, please check client!");
out.flush();
report.reportErrorLog(unique, "Sending an empty enviroment file!");
return false;
}
String name = directory + unique + "_" + credFrom + FormatConstants.ENVFORMAT;
bw = makeFile(name);
//we have created the file, we know that now a env will come, lets read it and save it!
while(input.compareTo(endEnv) != 0){
try {
bw.write(input);
bw.write(FormatConstants.NEWLINE);
} catch (IOException e) {
out.println("false\nIOException while saving enviroment file");
out.flush();
report.reportErrorLog(unique, "IOException while writing file " + unique + "_" + credFrom + ".env");
try {
bw.close();
} catch (IOException e1) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
if(!readLine())
return false;
}
//we are not closing the bw, because the resource should be saved here as well!
out.println(LET);
out.flush();
if(readLine())
return false;
return true;
}
private boolean getResource(boolean wasRead){
if(!wasRead){
if(!readLine()){
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
}
//all clients need to give a resource!
if(input.compareTo(resource) != 0){
report.reportDebugLog(unique, "Unable to get the resource!");
out.println("false\nNo resource given!");
out.flush();
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
if(!readLine()){
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
resourceSearched = input;
try {
bw.write(input);
bw.write(FormatConstants.NEWLINE);
} catch (IOException e) {
out.println("false\nIOException while saving resource file");
out.flush();
report.reportErrorLog(unique, "IOException while writing the resource file");
try {
bw.close();
} catch (IOException e1) {
out.println("false\nIOException while saving credential");
out.flush();
report.reportDebugLog(unique, "IOException while closing the credential file!");
return false;
}
return false;
}
try {
bw.close();
} catch (IOException e) {
out.println("false\nIOException while saving resource file");
out.flush();
report.reportErrorLog(unique, "IOException while closing the resource file");
return false;
}
if(!readLine())
return false;
if(input.compareTo(endResource) != 0){
report.reportErrorLog(unique, "message");
return false;
}
out.println(LET);
out.flush();
if(!readLine())
return false;
return true;
}
private boolean versions(boolean wasRead){
if(!wasRead){
if(!readLine())
return false;
}
if(input.startsWith("version:")){
//the version is given by the form version:xx, where xx is the version, that means it starts on the 8 char
clientVersion = input.substring(8);
//now we answer with our own server version
out.println("version:" + currentVersion);
out.flush();
if(!readLine())
return false;
return true;
}
return true;
}
private boolean createUnique(){
getUnique();
directory = new File(config.getTempDir(), "querry");
File dir = new File(directory, unique);
if(dir.isDirectory()){
out.println("false\nIOException while creating the directory!");
out.flush();
report.reportErrorLog(unique, "Directory " + unique + " exists");
return false;
}
if(!dir.canWrite()){
out.println("false\nIOException while creating the directory!");
out.flush();
report.reportErrorLog(unique, "Cannot write in " + config.getTempDir().getAbsolutePath());
return false;
}
if(!dir.mkdir()){
out.println("false\nIOException while creating the directory!");
out.flush();
report.reportErrorLog(unique, "Error while creating directory: " + unique);
return false;
}
return true;
}
private void getUnique(){
unique = String.format("%8d",config.getNextUnique());
}
/**
* Creates a file in the current unique directory. Because the name of the file is
* of type <unique>.<file extension> of the current process, we need to give the file
* extension
* @param fileName The name of the file that need to be saved.
* @return By success returns a <b>BufferedWriter</b> pointed at the filename,<br>
* else returns <b>null</b>
*/
private BufferedWriter makeFile(String fileName){
File file = new File(fileName);
if(file.exists()){
out.println("false\nIOException while creating temp file!");
out.flush();
report.reportErrorLog(unique, file.getName() + " exists!");
return null;
}
try {
bw = new BufferedWriter(new FileWriter(file));
} catch (IOException e) {
out.println("false\nIOException while creating the temp file!");
out.flush();
report.reportErrorLog(unique, "IOException while creating the file " + file.getName());
return null;
}
return bw;
}
private boolean getCredFrom(String directory){
File dir = new File(directory);
if(!dir.isDirectory()){
out.println("false\nUnable to create temp file.");
out.flush();
report.reportErrorLog("server", "Unable to create status file in " + unique);
return false;
}
File[] files = dir.listFiles();
if(files.length == 0){
credFrom = 0;
return true;
}
int i = 0, j = 0;
String name;
for(File file : files){
if(file.getName().endsWith(FormatConstants.CREDFORMAT)){
name = file.getName();
j = Integer.parseInt(name.substring(name.indexOf("_"), name.indexOf(FormatConstants.CREDFORMAT)));
if(j >= i){
i = j;
i++;
}
}
}
if(i == 0){
for(File file : files){
if(file.getName().compareTo(unique + FormatConstants.KEYFORMAT) == 0){
i = 1;
break;
}
}
}
credFrom = i;
return true;
}
}