Clean up code, support JSON
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| package xyz.maxwellj.chookpen | ||||
| package xyz.maxwellj.chookchat | ||||
|  | ||||
| import io.javalin.Javalin | ||||
| import io.javalin.websocket.WsContext | ||||
| @@ -7,6 +7,10 @@ import java.util.UUID | ||||
|  | ||||
| import kotlin.concurrent.fixedRateTimer | ||||
|  | ||||
| import org.json.JSONObject | ||||
| import org.json.JSONArray | ||||
| import org.json.JSONException | ||||
|  | ||||
| import java.io.File | ||||
| import java.io.BufferedReader | ||||
|  | ||||
| @@ -20,13 +24,26 @@ fun md5(input:String): String { | ||||
|     val md = MessageDigest.getInstance("MD5") | ||||
|     return BigInteger(1, md.digest(input.toByteArray())).toString(16).padStart(32, '0') | ||||
| } | ||||
|  | ||||
| /* | ||||
| fun removeLines(fileName: String, lineNumber: String) { | ||||
|     require(!fileName.isEmpty() && startLine >= 1 && numLines >= 1) | ||||
|     val f = File(fileName) | ||||
|     var lines = f.readLines() | ||||
|     if (startLine > size) { | ||||
|         println("The starting line is beyond the length of the file") | ||||
|         return | ||||
|     } | ||||
|     lines = lines.take(startLine - 1) + lines.drop(startLine + n - 1) | ||||
|     val text = lines.joinToString(System.lineSeparator()) | ||||
|     f.writeText(text) | ||||
| } | ||||
| */ | ||||
| object WsSessionManager { | ||||
|     var peopleOnline = mutableListOf("") | ||||
|     var sessionsList = mutableListOf("") | ||||
|  | ||||
|     private val sessions = ConcurrentHashMap<WsContext, String>() | ||||
|     private val sessionIds = ConcurrentHashMap<String, WsContext>() | ||||
|     val peopleOnline = mutableListOf("") | ||||
|     val sessionsList = mutableListOf("") | ||||
|     val sessions = ConcurrentHashMap<WsContext, String>() | ||||
|     val sessionIds = ConcurrentHashMap<String, WsContext>() | ||||
|     val userSessions = ConcurrentHashMap<String, String>() | ||||
|  | ||||
|     init { | ||||
|         fixedRateTimer("websocket-ping", period = 5000) { | ||||
| @@ -54,18 +71,25 @@ object WsSessionManager { | ||||
|     } | ||||
|  | ||||
|     fun broadcastOnlineUsers() { | ||||
|         broadcast("!users:{${peopleOnline.joinToString(",")}}") | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "users") | ||||
|             put("username", "system") | ||||
|             put("content", peopleOnline.joinToString(", ")) | ||||
|         } | ||||
|         broadcast(processedData.toString()) | ||||
|     } | ||||
|  | ||||
|     fun handleUserLogin(username: String) { | ||||
|         peopleOnline += username | ||||
|         broadcastOnlineUsers() | ||||
|         if (!peopleOnline.contains(username)) { | ||||
|             peopleOnline.add(username) | ||||
|             broadcastOnlineUsers() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun addSession(ctx: WsContext) { | ||||
|         try { | ||||
|             val sessionId = UUID.randomUUID().toString() | ||||
|             sessionsList += sessionId | ||||
|             sessionsList.add(sessionId)  // Changed from += to add() | ||||
|             sessions[ctx] = sessionId | ||||
|             sessionIds[sessionId] = ctx | ||||
|         } catch (e: Exception) { | ||||
| @@ -77,8 +101,13 @@ object WsSessionManager { | ||||
|         try { | ||||
|             val sessionId = sessions[ctx] | ||||
|             if (sessionId != null) { | ||||
|                 peopleOnline.removeAt(sessionsList.indexOf(sessionId)) | ||||
|                 sessionsList.removeAt(sessionsList.indexOf(sessionId)) | ||||
|                 // Find and remove the username associated with this session | ||||
|                 userSessions.entries.find { it.value == sessionId }?.let { entry -> | ||||
|                     peopleOnline.remove(entry.key) | ||||
|                     userSessions.remove(entry.key) | ||||
|                 } | ||||
|  | ||||
|                 sessionsList.remove(sessionId) | ||||
|                 sessions.remove(ctx) | ||||
|                 sessionIds.remove(sessionId) | ||||
|                 broadcastOnlineUsers() | ||||
| @@ -88,6 +117,13 @@ object WsSessionManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun associateUserWithSession(username: String, ctx: WsContext) { | ||||
|         val sessionId = sessions[ctx] | ||||
|         if (sessionId != null) { | ||||
|             userSessions[username] = sessionId | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun broadcast(message: String) { | ||||
|         val deadSessions = mutableListOf<WsContext>() | ||||
|          | ||||
| @@ -111,69 +147,36 @@ object WsSessionManager { | ||||
|     fun getSessionCount(): Int = sessions.size | ||||
| } | ||||
|  | ||||
| fun extractMessageContent(inputData: String): String { | ||||
|     var username = "" | ||||
|     var message = "" | ||||
|     var dataType = "" | ||||
|     var isParsingData = 0 | ||||
|      | ||||
|     for (char in inputData) { | ||||
|         if (char == ':') { | ||||
|             isParsingData = 1 | ||||
|         } else if (isParsingData == 1) { | ||||
|             if (char == '}') { | ||||
|                 isParsingData = 0 | ||||
|                 dataType = "" | ||||
|             } else if (char != '{') { | ||||
|                 if (dataType == "username") { | ||||
|                     username += char | ||||
|                 } else if (dataType == "message") { | ||||
|                     message += char | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             dataType += char | ||||
| fun extractMessageContent(inputData: String, ctx: WsContext): String { | ||||
|     val jsonInputData = JSONObject(inputData) | ||||
|     if (jsonInputData.getString("type") == "connect") { | ||||
|         val username = jsonInputData.getString("username") | ||||
|         WsSessionManager.associateUserWithSession(username, ctx) | ||||
|         WsSessionManager.handleUserLogin(username) | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "connect") | ||||
|             put("username", "system") | ||||
|             put("content", "${jsonInputData.getString("username")} just joined the room!") | ||||
|         } | ||||
|         return(processedData.toString()) | ||||
|     } | ||||
|      | ||||
|     return("$username: $message") | ||||
|     val processedData = JSONObject().apply { | ||||
|         put("type", jsonInputData.getString("type")) | ||||
|         put("username", jsonInputData.getString("username")) | ||||
|         put("content", jsonInputData.getString("content")) | ||||
|     }     | ||||
|     return(processedData.toString()) | ||||
| } | ||||
|  | ||||
| fun handleSentMessage(inputData: String): String { | ||||
|     println("API request recieved: $inputData") | ||||
|     // Parse data sent to the server by client | ||||
|     var username = "" | ||||
|     var token = "" | ||||
|     var message = "" | ||||
|     var dataType = "" | ||||
|     var command = "" | ||||
|     var commandArg = "" | ||||
|     var isParsingData = 0 | ||||
|     for (char in inputData) { | ||||
|         val character = char | ||||
|         if (character == ':') { | ||||
|             isParsingData = 1 | ||||
|         } else if (isParsingData == 1) { | ||||
|             if (character == '}') { | ||||
|                 isParsingData = 0 | ||||
|                 dataType = "" | ||||
|             } else if (character != '{') { | ||||
|                 if (dataType == "username") { | ||||
|                     username += character | ||||
|                 } else if (dataType == "token") { | ||||
|                     token += character | ||||
|                 } else if (dataType == "message") { | ||||
|                     message += character | ||||
|                 } else if (dataType == "command") { | ||||
|                     command += character | ||||
|                 } else if (dataType == "commandArg") { | ||||
|                     commandArg += character | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             dataType += character | ||||
|         } | ||||
|     } | ||||
|     var jsonInputData: JSONObject | ||||
|     try {jsonInputData = JSONObject(inputData)} catch (error: JSONException){return(error.toString())} | ||||
|      | ||||
|     val username = jsonInputData.getString("username") | ||||
|     val token = jsonInputData.getString("token") | ||||
|     val content = jsonInputData.getString("content") | ||||
|  | ||||
|     val userDatabaseParser = BufferedReader(File("userDatabase").reader()) | ||||
|     var lineNumber = 1 | ||||
|     var userLine = "" | ||||
| @@ -188,12 +191,18 @@ fun handleSentMessage(inputData: String): String { | ||||
|     userDatabaseParser.close() | ||||
|  | ||||
|     if (userLine == "") { | ||||
|         return("That account does not exist on this server.") | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "error") | ||||
|             put("username", "system") | ||||
|             put("content", "unknown-account") | ||||
|         }     | ||||
|         return(processedData.toString()) | ||||
|     } | ||||
|   | ||||
|     var usernameInDatabase = "" | ||||
|     var tokenInDatabase = "" | ||||
|     var saltInDatabase = "" | ||||
|     var banStatus = "" | ||||
|     var currentStage = 0 | ||||
|     for (char in userLine) { | ||||
|         if (char == ':') { | ||||
| @@ -205,103 +214,46 @@ fun handleSentMessage(inputData: String): String { | ||||
|             tokenInDatabase += char | ||||
|         } else if (currentStage == 2) { | ||||
|             saltInDatabase += char | ||||
|         } else if (currentStage == 3) { | ||||
|             banStatus += char | ||||
|         } | ||||
|     } | ||||
|     tokenInDatabase = tokenInDatabase.replace(":", "") | ||||
|     saltInDatabase = saltInDatabase.replace(":", "") | ||||
|     banStatus = banStatus.replace(":", "") | ||||
|     if (banStatus == "1") { | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "error") | ||||
|             put("username", "system") | ||||
|             put("content", "banned") | ||||
|         }     | ||||
|         return(processedData.toString()) | ||||
|     } | ||||
|     val tokenWithSalt = (md5(token + saltInDatabase)) | ||||
|     /*println(saltInDatabase) | ||||
|     println(tokenWithSalt) | ||||
|     if (tokenWithSalt != tokenInDatabase)  {*/ | ||||
|     if (token != tokenInDatabase) { | ||||
|         return("Invalid token! Please try putting in your password right") | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "error") | ||||
|             put("username", "system") | ||||
|             put("content", "invalid-token") | ||||
|         } | ||||
|         return(processedData.toString()) | ||||
|     } | ||||
|     // Make the message to respond to the client | ||||
|     val chatHistoryView = File("chatHistory") | ||||
|     var fullMessage = "" | ||||
|     if (message != "") { | ||||
|         fullMessage = "${chatHistoryView.readText()}$username: $message" | ||||
|     if (content != "") { | ||||
|         fullMessage = "${chatHistoryView.readText()}$username: $content" | ||||
|         // Add the client's message to the chat history | ||||
|         val chatHistory = File("chatHistory") | ||||
|         chatHistory.appendText("$username: $message ${System.lineSeparator()}") | ||||
|         message = "" | ||||
|         chatHistory.appendText("$username: $content ${System.lineSeparator()}") | ||||
|         return("Success") | ||||
|     } else if (command != "") { | ||||
|         if (command == "sync") { | ||||
|             return(chatHistoryView.readText()) | ||||
|         } else if (command == "login") { | ||||
|             WsSessionManager.handleUserLogin(commandArg) | ||||
|             return("Login successful") | ||||
|         } | ||||
|     } else { | ||||
|         return("No data provided") | ||||
|     } | ||||
|     return("System: Welcome to Chookpen, $username!") | ||||
| } | ||||
|  | ||||
| fun syncMessages(inputData: String): String { | ||||
|     println("API request recieved: $inputData") | ||||
|     // Parse data sent to the server by client | ||||
|     var username = "" | ||||
|     var token = "" | ||||
|     var dataType = "" | ||||
|     var isParsingData = 0 | ||||
|     for (char in inputData) { | ||||
|         val character = char | ||||
|         if (character == ':') { | ||||
|             isParsingData = 1 | ||||
|         } else if (isParsingData == 1) { | ||||
|             if (character == '}') { | ||||
|                 isParsingData = 0 | ||||
|                 dataType = "" | ||||
|             } else if (character != '{') { | ||||
|                 if (dataType == "username") { | ||||
|                     username += character | ||||
|                 } else if (dataType == "token") { | ||||
|                     token += character | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             dataType += character | ||||
|         } | ||||
|     } | ||||
|     val userDatabaseParser = BufferedReader(File("userDatabase").reader()) | ||||
|     var lineNumber = 1 | ||||
|     var userLine = "" | ||||
|      | ||||
|     // Search the user database to find required information about the user | ||||
|     userDatabaseParser.forEachLine { line -> | ||||
|         if (line.contains(username)) { | ||||
|             userLine = line | ||||
|         } | ||||
|         lineNumber++ | ||||
|     } | ||||
|     userDatabaseParser.close() | ||||
|  | ||||
|     if (userLine == "") { | ||||
|         return("Account not found") | ||||
|     } | ||||
|   | ||||
|     var usernameInDatabase = "" | ||||
|     var tokenInDatabase = "" | ||||
|     var currentStage = 0 | ||||
|     for (char in userLine) { | ||||
|         if (char == ':') { | ||||
|             currentStage ++ | ||||
|         } | ||||
|         if (currentStage == 0) { | ||||
|             usernameInDatabase += char | ||||
|         } else if (currentStage == 1) { | ||||
|             tokenInDatabase += char | ||||
|         } | ||||
|     } | ||||
|     tokenInDatabase = tokenInDatabase.replace(":", "") | ||||
|     if (token != tokenInDatabase)  { | ||||
|         return("Invalid token") | ||||
|     } | ||||
|     // Send back message history  | ||||
|     val chatHistoryView = File("chatHistory") | ||||
|     return(chatHistoryView.readText()) | ||||
|     return("Chookchat") | ||||
| } | ||||
|  | ||||
| fun createAccount(inputData: String): String { | ||||
| @@ -341,7 +293,12 @@ fun createAccount(inputData: String): String { | ||||
|     var response = "" | ||||
|     userDatabaseParser.forEachLine { line -> | ||||
|         if (line.contains(username)) { | ||||
|             response = "Username already exists" | ||||
|             val processedData = JSONObject().apply { | ||||
|                 put("type", "error") | ||||
|                 put("username", "system") | ||||
|                 put("content", "username-taken") | ||||
|             } | ||||
|             response = processedData.toString()        | ||||
|         } | ||||
|         lineNumber++ | ||||
|     } | ||||
| @@ -350,101 +307,47 @@ fun createAccount(inputData: String): String { | ||||
|     } | ||||
|     userDatabaseParser.close() | ||||
|     if (username == "") { | ||||
|         return("No username") | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "error") | ||||
|             put("username", "system") | ||||
|             put("content", "no-username") | ||||
|         } | ||||
|         return(processedData.toString()) | ||||
|     } | ||||
|  | ||||
|     if (token == "") { | ||||
|         return("No token") | ||||
|         val processedData = JSONObject().apply { | ||||
|             put("type", "error") | ||||
|             put("username", "system") | ||||
|             put("content", "no-token") | ||||
|         } | ||||
|         return(processedData.toString()) | ||||
|     } | ||||
|  | ||||
|     val userDatabaseFile = File("userDatabase") | ||||
|     userDatabaseFile.appendText("${System.lineSeparator()}$username:$token") | ||||
|     return("Success") | ||||
|     val processedData = JSONObject().apply { | ||||
|         put("type", "success") | ||||
|         put("username", "system") | ||||
|         put("content", "success") | ||||
|     } | ||||
|     return(processedData.toString()) | ||||
| } | ||||
| fun authKey(inputData: String): String { | ||||
|     println("API request recieved: $inputData") | ||||
|         | ||||
|     // Parse data sent to the server by client | ||||
|     var username = "" | ||||
|     var token = "" | ||||
|     var authKey = "" | ||||
|     var dataType = "" | ||||
|     var isParsingData = 0 | ||||
|     for (char in inputData) { | ||||
|         val character = char | ||||
|         if (character == ':') { | ||||
|             isParsingData = 1 | ||||
|         } else if (isParsingData == 1) { | ||||
|             if (character == '}') { | ||||
|                 isParsingData = 0 | ||||
|                 dataType = "" | ||||
|             } else if (character != '{') { | ||||
|                 if (dataType == "username") { | ||||
|                     username += character | ||||
|                 } else if (dataType == "token") { | ||||
|                     token += character | ||||
|                 } else if (dataType == "authkey") { | ||||
|                     authKey += character | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             dataType += character | ||||
|         } | ||||
|     } | ||||
|     val userDatabaseParser = BufferedReader(File("userDatabase").reader()) | ||||
|     var lineNumber = 1 | ||||
|     var userLine = "" | ||||
|      | ||||
|     // Search the user database to find required information about the user | ||||
|     userDatabaseParser.forEachLine { line -> | ||||
|         if (line.contains(username)) { | ||||
|             userLine = line | ||||
|         } | ||||
|         lineNumber++ | ||||
|     } | ||||
|     userDatabaseParser.close() | ||||
|  | ||||
|     if (userLine == "") { | ||||
|         return("Account not found") | ||||
|     } | ||||
|   | ||||
|     var usernameInDatabase = "" | ||||
|     var tokenInDatabase = "" | ||||
| fun handleServerCommand(command: String): String { | ||||
|     val commandArgs = mutableListOf("") | ||||
|     commandArgs.drop(1) | ||||
|     var currentStage = 0 | ||||
|     for (char in userLine) { | ||||
|         if (char == ':') { | ||||
|             currentStage ++ | ||||
|         } | ||||
|         if (currentStage == 0) { | ||||
|             usernameInDatabase += char | ||||
|         } else if (currentStage == 1) { | ||||
|             tokenInDatabase += char | ||||
|         } | ||||
|     } | ||||
|     tokenInDatabase = tokenInDatabase.replace(":", "") | ||||
|     if (token != tokenInDatabase)  { | ||||
|         return("Invalid token") | ||||
|     } | ||||
|     if (authKey == "") { | ||||
|         return("No auth key provided") | ||||
|     } | ||||
|     // Make the message to respond to the client | ||||
|     val chatHistoryView = File("chatHistory") | ||||
|     var fullMessage = "" | ||||
|     if (authKey != "") { | ||||
|         fullMessage = "encryptionKey:$username:$authKey" | ||||
|         authKey = "" | ||||
|     } else { | ||||
|         fullMessage = "${chatHistoryView.readText()}" | ||||
|     } | ||||
|     val response = if (inputData.isNotEmpty()) { | ||||
|         fullMessage             | ||||
|     } else { | ||||
|         "No data provided" | ||||
|     } | ||||
|  | ||||
|     // Send the message to the client | ||||
|     return("Success") | ||||
|     for (char in command) { | ||||
|         if (char == ' ') { | ||||
|             currentStage ++ | ||||
|             commandArgs += "" | ||||
|         } else { | ||||
|             commandArgs[currentStage] += char | ||||
|         } | ||||
|     } | ||||
|     return("I'm not sure how to ${commandArgs.toString()}") | ||||
| } | ||||
|  | ||||
| fun main(args: Array<String>) { | ||||
| @@ -455,18 +358,7 @@ fun main(args: Array<String>) { | ||||
|         }.get("/") { ctx ->  | ||||
|             ctx.redirect("/index.html")  | ||||
|         } | ||||
|         .get("/api/send/{content}") { ctx ->  | ||||
|             val result = handleSentMessage(ctx.pathParam("content")) | ||||
|             if (result == "Success") { | ||||
|                 val messageContent = extractMessageContent(ctx.pathParam("content")) | ||||
|                 WsSessionManager.broadcast(messageContent) | ||||
|             } | ||||
|             ctx.result(result) | ||||
|         } | ||||
|         .get("/api/createaccount/{content}") { ctx -> ctx.result(createAccount(ctx.pathParam("content")))} | ||||
|         .get("/api/syncmessages/{content}") { ctx -> ctx.result(syncMessages(ctx.pathParam("content")))} | ||||
|         .get("/api/authkey/{content}") { ctx -> ctx.result(authKey(ctx.pathParam("content")))} | ||||
|          | ||||
|         .ws("/api/websocket") { ws ->  | ||||
|             ws.onConnect { ctx ->  | ||||
|                 WsSessionManager.addSession(ctx) | ||||
| @@ -487,7 +379,7 @@ fun main(args: Array<String>) { | ||||
|                             println("Error sending error message: ${e.message}") | ||||
|                         } | ||||
|                     } else { | ||||
|                         val messageContent = extractMessageContent(ctx.message()) | ||||
|                         val messageContent = extractMessageContent(ctx.message(), ctx) | ||||
|                         WsSessionManager.broadcast(messageContent) | ||||
|                     } | ||||
|                 } | ||||
| @@ -495,6 +387,9 @@ fun main(args: Array<String>) { | ||||
|             } | ||||
|         } | ||||
|     .start(7070) | ||||
|  | ||||
|     println("Type a command for the server") | ||||
|     while (1 == 1) { | ||||
|         println(handleServerCommand(readln())) | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								server/src/main/resources/public/gradient.css
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								server/src/main/resources/public/gradient.css
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../../../../client-web/gradient.css | ||||
		Reference in New Issue
	
	Block a user
	 Maxwell Jeffress
					Maxwell Jeffress