using System; using System.Collections.Generic; using System.Net; using System.Text; using System.Text.RegularExpressions; using FilterAPI; using FilterLauncher.LauncherNetworking; using Nancy; using Nancy.ModelBinding; using Newtonsoft.Json; namespace FilterLauncher.LauncherNetworking { public class LauncherRestModule : NancyModule { static bool UsernameOK(String username) { return Injection.ContainsOnlyLetterOrDigit(username) && username.Length <= 32; } static bool RegPasswordOK(String password) { return password.Substring(0, 7).Equals("$2y$12$") && password.Length.Equals(60); } static bool LoginPasswordOK(String password) { return Regex.IsMatch(password, @"[0-9a-f]{32}", RegexOptions.IgnoreCase); } static bool EmailOK(String email) { return true; } static bool IPOK(String ip) { IPAddress address; return IPAddress.TryParse(ip, out address); } static bool GenBoolOK(String boolStr) { bool boolean; return Boolean.TryParse(boolStr, out boolean); } static bool GenInt32OK(String intStr) { int integer; return Int32.TryParse(intStr, out integer); } static bool GenStringOK(String str) { return Injection.ContainsOnlyLetterOrDigit(str); } static bool AuthTokenOK(String token) { return token.Equals(Program.Conf.GetConfigValue("LauncherAuthToken")); } public LauncherRestModule() : base("/api/v1/filter") { Get["/ping"] = x => { InfoResponse genResp = new InfoResponse { StatusCode = Nancy.HttpStatusCode.OK, Response = "Pong", Info = "Pong" }; return JsonResponse.GetNancyResp(genResp); }; Get["/inforeq/{srvid}/{infotype}/{authtoken}"] = parameters => { InfoRequest infoReq = this.Bind(); InfoResponse infoResp = new InfoResponse(); String SrvID = infoReq.SrvID; String InfoType = infoReq.InfoType; String AuthToken = infoReq.AuthToken; if (!AuthTokenOK(AuthToken)) { infoResp.StatusCode = Nancy.HttpStatusCode.Forbidden; infoResp.Response = "Bad Token"; } else if (GenInt32OK(SrvID) && GenStringOK(InfoType)) { try { switch (InfoType) { case "LauncherTitle": infoResp.Info = Program.Conf.GetConfigValue("LauncherTitle"); break; case "LauncherPatchesURL": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}LPURL"); break; case "LauncherVersionFN": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}LVFN"); break; case "LauncherPatchLogFN": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}LPLFN"); break; case "GamePatchesURL": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}GPURL"); break; case "GameVersionFN": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}GVFN"); break; case "GamePatchLogFN": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}GPLFN"); break; case "GameServerIP": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}GSIPURL"); break; case "GameServerName": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}GSN"); break; case "GameClientName": infoResp.Info = Program.Conf.GetConfigValue($"LauncherS{SrvID}GCN"); break; default: infoResp.Info = ""; break; } infoResp.StatusCode = infoResp.Info.Equals("") ? Nancy.HttpStatusCode.BadRequest : Nancy.HttpStatusCode.OK; infoResp.Response = infoResp.Info.Equals("") ? "NoInfo" : "InfoGood"; } catch (KeyNotFoundException ex) { infoResp.StatusCode = Nancy.HttpStatusCode.BadRequest; infoResp.Response = $"Bad Request: {ex.Message}"; } } return JsonResponse.GetNancyResp(infoResp); }; Post["/maintenance"] = parameters => { InfoRequest genReq = this.Bind(); GenericResponse genResp = new GenericResponse(); String AuthToken = genReq.AuthToken; if (!AuthTokenOK(AuthToken)) { genResp.StatusCode = Nancy.HttpStatusCode.Forbidden; genResp.Response = "Bad Token"; } else { } return JsonResponse.GetNancyResp(genResp); }; Post["/user/login"] = parameters => { LoginRequest loginReq = this.Bind(); LoginResp loginResp = new LoginResp { Token = null }; String sUsername = loginReq.Username; String sUserPass = loginReq.Password; String AuthToken = loginReq.AuthToken; if (!AuthTokenOK(AuthToken)) { loginResp.StatusCode = Nancy.HttpStatusCode.Forbidden; loginResp.Response = "Bad Token"; } if (UsernameOK(sUsername) && LoginPasswordOK(sUserPass)) { if (!Program.SQL.sUsernameExists(sUsername)) { loginResp.StatusCode = Nancy.HttpStatusCode.BadRequest; loginResp.Response = "Username/Password is incorrect"; Program.SendConsoleText(ConsoleColor.Gray, $"Attempted login. sUsername: {sUsername} - account does not exist."); } else { var dbPass = Program.SQL.GetsUserPassFromsUsername(sUsername); if (BCrypt.Net.BCrypt.Verify(sUserPass, dbPass)) { Int32 nEMID = Program.SQL.GetnEMIDFromsUsername(sUsername); String sToken = Program.SQL.HandleTokenFornEMID(nEMID); loginResp.StatusCode = Nancy.HttpStatusCode.OK; loginResp.Response = $"Success"; loginResp.Token = sToken; Program.SendConsoleText(ConsoleColor.Gray, $"Successful login. sUsername: {sUsername}, nEMID: {nEMID}, sToken: {sToken}"); } else { loginResp.StatusCode = Nancy.HttpStatusCode.BadRequest; loginResp.Response = "Username/Password is incorrect"; Program.SendConsoleText(ConsoleColor.Gray, "Username/Password was entered incorrectly with {0} as username.", sUsername); } } } else { loginResp.StatusCode = Nancy.HttpStatusCode.BadRequest; loginResp.Response = "Username/Password is incorrect"; Program.SendConsoleText(ConsoleColor.Gray, "Username/Password was entered incorrectly with {0} as username.", sUsername); } return JsonResponse.GetNancyResp(loginResp); }; Post["/user/register"] = parameters => { RegisterRequest regReq = this.Bind(); GenericResponse regResp = new GenericResponse(); String sUsername = regReq.Username.ToLower(); String sUserPass = regReq.Password; String sEmail = regReq.Email; String sIP = regReq.IP; String AuthToken = regReq.AuthToken; if (!AuthTokenOK(AuthToken)) { regResp.StatusCode = Nancy.HttpStatusCode.Forbidden; regResp.Response = "Bad Token"; } else if (UsernameOK(sUsername) && RegPasswordOK(sUserPass) && EmailOK(sEmail) && IPOK(sIP)) { if (Program.SQL.sUsernameExists(sUsername)) { regResp.StatusCode = Nancy.HttpStatusCode.BadRequest; regResp.Response = "Account already exists."; Program.SendConsoleText(ConsoleColor.Gray, "New register from '{0}', but username was taken.", sUsername); } else { regResp.StatusCode = Nancy.HttpStatusCode.OK; regResp.Response = String.Format("Account added. Username: {0}. Password: {1}. Email: {2}. IP: {3} ", sUsername, sUserPass, sEmail, sIP); Program.SQL.InsertNewtAccount(sUsername, sUserPass, sEmail, sIP, 0); Program.SendConsoleText(ConsoleColor.Gray, "New account registered. Username: {0}. Password: {1}. Email: {2}. IP: {3} ", sUsername, sUserPass, sEmail, sIP); } } else { regResp.StatusCode = Nancy.HttpStatusCode.BadRequest; regResp.Response = "Bad or Missing Parameters"; } return JsonResponse.GetNancyResp(regResp); }; Post["/user/{username}/set/nAuthID/{nAuthID}/{token}"] = parameters => { GenericResponse genResp = new GenericResponse(); String Username = parameters.username; String NAuthID = parameters.nAuthID; String Token = parameters.token; if (!AuthTokenOK(Token)) { genResp.StatusCode = Nancy.HttpStatusCode.Forbidden; genResp.Response = "Bad Token"; } else if (UsernameOK(Username) && GenInt32OK(NAuthID)) { byte nAuthID = Byte.Parse(NAuthID); if (!Program.SQL.sUsernameExists(Username)) { genResp.StatusCode = Nancy.HttpStatusCode.BadRequest; genResp.Response = "Account does not exist."; Program.SendConsoleText(ConsoleColor.Gray, $"Attempted to set nAuthID '{nAuthID}' for '{Username}', but account does not exist."); } else { Program.SQL.SetnAuthIDFornEMID(Program.SQL.GetnEMIDFromsUsername(Username), nAuthID); genResp.StatusCode = Nancy.HttpStatusCode.OK; genResp.Response = $"Account nAuthID set to '{nAuthID}' for Username: {Username}."; Program.SendConsoleText(ConsoleColor.Gray, $"Account nAuthID set to '{nAuthID}' for Username: {Username}."); } } return JsonResponse.GetNancyResp(genResp); }; } } }