diff --git a/Admin/Application.csproj b/Admin/Application.csproj index ece9d59f..6f64bf84 100644 --- a/Admin/Application.csproj +++ b/Admin/Application.csproj @@ -158,6 +158,9 @@ Always + + PreserveNewest + PreserveNewest diff --git a/Admin/Kayak.cs b/Admin/Kayak.cs index 1a62ccbc..12dc5213 100644 --- a/Admin/Kayak.cs +++ b/Admin/Kayak.cs @@ -45,23 +45,41 @@ namespace IW4MAdmin querySet = System.Web.HttpUtility.ParseQueryString(SharedLibrary.Utilities.StripIllegalCharacters(request.QueryString)); querySet.Set("IP", IP); - SharedLibrary.HttpResponse requestedPage = WebService.GetPage(request.Path, querySet, request.Headers); - var headers = new HttpResponseHead() + try { - Status = "200 OK", - Headers = new Dictionary() + SharedLibrary.HttpResponse requestedPage = WebService.GetPage(request.Path, querySet, request.Headers); + + var headers = new HttpResponseHead() + { + Status = "200 OK", + Headers = new Dictionary() { { "Content-Type", requestedPage.contentType }, { "Content-Length", requestedPage.content.Length.ToString() }, { "Access-Control-Allow-Origin", "*" }, } - }; + }; - foreach (var key in requestedPage.additionalHeaders.Keys) - headers.Headers.Add(key, requestedPage.additionalHeaders[key]); + foreach (var key in requestedPage.additionalHeaders.Keys) + headers.Headers.Add(key, requestedPage.additionalHeaders[key]); - response.OnResponse(headers, new BufferedProducer(requestedPage.content)); + response.OnResponse(headers, new BufferedProducer(requestedPage.content)); + } + + catch (Exception e) + { + ApplicationManager.GetInstance().Logger.WriteError($"Webfront error during request: {e.Message}"); + response.OnResponse(new HttpResponseHead() + { + Status = "500 Internal Server Error", + Headers = new Dictionary() + { + { "Content-Type", "text/html" }, + { "Content-Length", "0"}, + } + }, new BufferedProducer("")); + } } } diff --git a/Admin/Manager.cs b/Admin/Manager.cs index 13f40b20..7a90b3b7 100644 --- a/Admin/Manager.cs +++ b/Admin/Manager.cs @@ -161,6 +161,7 @@ namespace IW4MAdmin Commands.Add(new CExecuteRCON("rcon", "send rcon command to server. syntax: !rcon ", "rcon", Player.Permission.Owner, 1, false)); Commands.Add(new CFindAllPlayers("findall", "find a player by their aliase(s). syntax: !findall ", "fa", Player.Permission.Moderator, 1, false)); Commands.Add(new CPlugins("plugins", "view all loaded plugins. syntax: !plugins", "p", Player.Permission.Administrator, 0, false)); + Commands.Add(new CIP("ip", "view your external IP address. syntax: !ip", "getexternalip", Player.Permission.User, 0, false)); foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands) Commands.Add(C); @@ -247,5 +248,49 @@ namespace IW4MAdmin return ActiveClients; } + + public IList GetAliasClients(Player Origin) + { + List databaseIDs = new List(); + + foreach (Aliases A in GetAliases(Origin)) + databaseIDs.Add(A.Number); + + return GetClientDatabase().GetPlayers(databaseIDs); + } + + public IList GetAliases(Player Origin) + { + List allAliases = new List(); + + if (Origin == null) + return allAliases; + + Aliases currentIdentityAliases = GetAliasesDatabase().GetPlayerAliases(Origin.DatabaseID); + + if (currentIdentityAliases == null) + return allAliases; + + GetAliases(allAliases, currentIdentityAliases); + if (Origin.Alias != null) + allAliases.Add(Origin.Alias); + return allAliases; + } + + private void GetAliases(List returnAliases, Aliases currentAlias) + { + foreach (String IP in currentAlias.IPS) + { + List Matching = GetAliasesDatabase().GetPlayerAliases(IP); + foreach (Aliases I in Matching) + { + if (!returnAliases.Contains(I) && returnAliases.Find(x => x.Number == I.Number) == null) + { + returnAliases.Add(I); + GetAliases(returnAliases, I); + } + } + } + } } } diff --git a/Admin/Server.cs b/Admin/Server.cs index 5252fabb..d576a746 100644 --- a/Admin/Server.cs +++ b/Admin/Server.cs @@ -15,40 +15,6 @@ namespace IW4MAdmin { public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { } - private void GetAliases(List returnAliases, Aliases currentAlias) - { - foreach(String IP in currentAlias.IPS) - { - List Matching = Manager.GetAliasesDatabase().GetPlayerAliases(IP); - foreach(Aliases I in Matching) - { - if (!returnAliases.Contains(I) && returnAliases.Find(x => x.Number == I.Number) == null) - { - returnAliases.Add(I); - GetAliases(returnAliases, I); - } - } - } - } - - public override List GetAliases(Player Origin) - { - List allAliases = new List(); - - if (Origin == null) - return allAliases; - - Aliases currentIdentityAliases = Manager.GetAliasesDatabase().GetPlayerAliases(Origin.DatabaseID); - - if (currentIdentityAliases == null) - return allAliases; - - GetAliases(allAliases, currentIdentityAliases); - if (Origin.Alias != null) - allAliases.Add(Origin.Alias); - return allAliases; - } - override public async Task AddPlayer(Player P) { if (P.ClientID < 0 || P.ClientID > (Players.Count-1) || P.Ping < 1 || P.Ping == 999) // invalid index @@ -123,7 +89,7 @@ namespace IW4MAdmin return true; } - var newPlayerAliases = GetPlayerAliases(NewPlayer); + var newPlayerAliases = Manager.GetAliasClients(NewPlayer); foreach (Player aP in newPlayerAliases) // lets check their aliases { @@ -153,7 +119,6 @@ namespace IW4MAdmin if (NewPlayer.Level > Player.Permission.Moderator) await NewPlayer.Tell("There are ^5" + Reports.Count + " ^7recent reports!"); - ClientNum++; return true; } @@ -168,7 +133,7 @@ namespace IW4MAdmin //Remove player by CLIENT NUMBER override public async Task RemovePlayer(int cNum) { - if (cNum >= 0 && cNum < Players.Count) + if (cNum >= 0) { Player Leaving = Players[cNum]; Leaving.Connections++; @@ -177,8 +142,6 @@ namespace IW4MAdmin Logger.WriteInfo($"Client {Leaving.Name}::{Leaving.NetworkID} disconnecting..."); await ExecuteEvent(new Event(Event.GType.Disconnect, "", Leaving, null, this)); Players[cNum] = null; - - ClientNum--; } } @@ -315,7 +278,7 @@ namespace IW4MAdmin } } - async Task PollPlayersAsync() + async Task PollPlayersAsync() { var CurrentPlayers = await this.GetStatusAsync(); @@ -327,6 +290,8 @@ namespace IW4MAdmin foreach (var P in CurrentPlayers) await AddPlayer(P); + + return CurrentPlayers.Count; } long l_size = -1; @@ -349,7 +314,7 @@ namespace IW4MAdmin try { - await PollPlayersAsync(); + ClientNum = await PollPlayersAsync(); if (ConnectionErrors > 0) { @@ -389,10 +354,10 @@ namespace IW4MAdmin playerCountStart = DateTime.Now; } - if (LastMessage.TotalSeconds > MessageTime && BroadcastMessages.Count > 0 && Players.Count > 0) + if (LastMessage.TotalSeconds > MessageTime && BroadcastMessages.Count > 0 && ClientNum > 0) { - await Broadcast(Utilities.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])); - NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage++; + Console.WriteLine(Utilities.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])); + NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1; start = DateTime.Now; } diff --git a/Admin/WebService.cs b/Admin/WebService.cs index 11ad1d4b..ada77871 100644 --- a/Admin/WebService.cs +++ b/Admin/WebService.cs @@ -29,6 +29,8 @@ namespace IW4MAdmin SharedLibrary.WebService.PageList.Add(new WebConsole()); SharedLibrary.WebService.PageList.Add(new ConsoleJSON()); SharedLibrary.WebService.PageList.Add(new PubbansJSON()); + SharedLibrary.WebService.PageList.Add(new AdminsJSON()); + SharedLibrary.WebService.PageList.Add(new Admins()); Thread scheduleThread = new Thread(() => { ScheduleThreadStart(webScheduler, webService); }) { @@ -521,6 +523,69 @@ namespace IW4MAdmin } } + + class Admins : HTMLPage + { + public override string GetName() + { + return "Admins"; + } + + public override string GetPath() + { + return "/Admins"; + } + + public override string GetContent(System.Collections.Specialized.NameValueCollection querySet, IDictionary headers) + { + StringBuilder S = new StringBuilder(); + S.Append(LoadHeader()); + + IFile admins = new IFile("webfront\\admins.html"); + S.Append(admins.GetText()); + admins.Close(); + + S.Append(LoadFooter()); + + return S.ToString(); + } + } + + class AdminsJSON : IPage + { + public string GetName() + { + return "Admins Json"; + } + + public string GetPath() + { + return "/GetAdmins"; + } + + public HttpResponse GetPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary headers) + { + var Admins = ApplicationManager.GetInstance().GetClientDatabase().GetAdmins().OrderByDescending(a => a.Level); + HttpResponse resp = new HttpResponse() + { + contentType = GetContentType(), + content = Newtonsoft.Json.JsonConvert.SerializeObject(Admins, Newtonsoft.Json.Formatting.Indented), + additionalHeaders = new Dictionary() + }; + return resp; + } + + public string GetContentType() + { + return "application/json"; + } + + public bool Visible() + { + return false; + } + } + class PubbansJSON : IPage { public string GetName() @@ -604,7 +669,7 @@ namespace IW4MAdmin return false; } } - + class GetPlayer : IPage { public string GetContentType() @@ -631,7 +696,8 @@ namespace IW4MAdmin contentType = GetContentType(), additionalHeaders = new Dictionary() }; - bool authed = ApplicationManager.GetInstance().GetClientDatabase().GetAdmins().FindAll(x => x.IP == querySet["IP"]).Count > 0; + bool authed = ApplicationManager.GetInstance().GetClientDatabase().GetAdmins().FindAll(x => x.IP == querySet["IP"] && x.Level > Player.Permission.Trusted).Count > 0 + || querySet["IP"] == "127.0.0.1"; bool recent = false; if (querySet["id"] != null) @@ -675,7 +741,7 @@ namespace IW4MAdmin if (!recent) { - foreach (var a in ApplicationManager.GetInstance().Servers.First().GetAliases(pp)) + foreach (var a in ApplicationManager.GetInstance().GetAliases(pp)) { eachPlayer.playerAliases = a.Names; eachPlayer.playerIPs = a.IPS; @@ -765,4 +831,11 @@ namespace IW4MAdmin { public List Result; } + + [Serializable] + class PrivilegedUsers + { + public Player.Permission Permission { get; set; } + public List Players { get; set; } + } } \ No newline at end of file diff --git a/Admin/lib/SharedLibrary.dll b/Admin/lib/SharedLibrary.dll index 8d35f982..3421cb94 100644 Binary files a/Admin/lib/SharedLibrary.dll and b/Admin/lib/SharedLibrary.dll differ diff --git a/Admin/version.txt b/Admin/version.txt index 7fb91705..b3056056 100644 --- a/Admin/version.txt +++ b/Admin/version.txt @@ -3,8 +3,13 @@ CHANGELOG: -works: with COD, WaW, MW3, BO1 (preliminary without extensive testing) -fixed the issue with webfront chat history -fixed console issue of spamming 'polling rate decreased' when server goes offline --'unknown' admin in webfront defaults to 'IW4MAdmin' +-'unknown' admin in webfront defaults to 'IW4MAdmin' (refactoring mistake) -streamlined the async server initialization +-added !ip command (prints a client's external IP) +-fixed up the findall command +-moved aliases to the manager +-added admins page to view privileged users +-fixed refactoring mistake with messages VERSION 1.3 diff --git a/Admin/webfront/admins.html b/Admin/webfront/admins.html new file mode 100644 index 00000000..80f8886e --- /dev/null +++ b/Admin/webfront/admins.html @@ -0,0 +1,71 @@ + + +
+ + +
+ +
+
Name
+
Aliases
+
IP
+
Level
+
Connections
+
V2
+
Last Seen
+
+
+
+
diff --git a/README.md b/README.md index fbef347f..679cf820 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # IW4MAdmin ### Quick Start Guide -### Version 1.2 +### Version 1.4 _______ ### Setup @@ -46,7 +46,6 @@ ___ |owner |claim ownership of your server | False | 0 | None | |setlevel |set player to specified administration level | True | 2 | Owner | |rcon |send rcon command to server | False | 1 | Owner | -|reserve |give player reserved status to join on full server | True | 1 | Owner | |ban |permanently ban a player from the server | True | 2 | SeniorAdmin | |unban |unban player by database id | True | 1 | SeniorAdmin | |find |find player in the database | False | 1 | SeniorAdmin | diff --git a/SharedLibrary/Commands/NativeCommands.cs b/SharedLibrary/Commands/NativeCommands.cs index 35eaea8e..935eb7f2 100644 --- a/SharedLibrary/Commands/NativeCommands.cs +++ b/SharedLibrary/Commands/NativeCommands.cs @@ -18,7 +18,7 @@ namespace SharedLibrary.Commands } public class COwner : Command - { + { public COwner(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { } public override async Task ExecuteAsync(Event E) @@ -44,7 +44,7 @@ namespace SharedLibrary.Commands if (E.Origin.Level <= E.Target.Level) await E.Origin.Tell($"You do not have the required privileges to warn {E.Target.Name}"); else - await E.Target.Warn(E.Target.lastOffense, E.Origin); + await E.Target.Warn(E.Target.lastOffense, E.Origin); } } @@ -74,7 +74,7 @@ namespace SharedLibrary.Commands await E.Target.Kick(E.Target.lastOffense, E.Origin); } else - await E.Origin.Tell($"You do not have the required privileges to kick {E.Target.Name}"); + await E.Origin.Tell($"You do not have the required privileges to kick {E.Target.Name}"); } } @@ -216,7 +216,7 @@ namespace SharedLibrary.Commands List CommandList = E.Owner.Manager.GetCommands(); foreach (Command C in CommandList) - { + { if (E.Origin.Level >= C.Permission) { helpResponse.Append(" [^3" + C.Name + "^7] "); @@ -390,7 +390,7 @@ namespace SharedLibrary.Commands } foreach (Player P in db_players) - { + { String mesg = String.Format("[^3{0}^7] [^3@{1}^7] - [{2}^7] - {3} | last seen {4} ago", P.Name, P.DatabaseID, Utilities.ConvertLevelToColor(P.Level), P.IP, P.GetLastConnection()); await E.Origin.Tell(mesg); } @@ -426,7 +426,7 @@ namespace SharedLibrary.Commands String lookingFor = String.Empty; - foreach(String S in P.Names) + foreach (String S in P.Names) { if (S.Contains(E.Data)) lookingFor = S; @@ -434,7 +434,7 @@ namespace SharedLibrary.Commands Player Current = E.Owner.Manager.GetClientDatabase().GetPlayer(P.Number); - if (Current != null) + if (Current != null && Current.Name != lookingFor) { String mesg = String.Format("^1{0} ^7now goes by ^5{1}^7 [^3{2}^7]", lookingFor, Current.Name, Current.DatabaseID); await E.Origin.Tell(mesg); @@ -487,7 +487,7 @@ namespace SharedLibrary.Commands public CReload(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { } public override async Task ExecuteAsync(Event E) - { + { if (E.Owner.Reload()) await E.Origin.Tell("Sucessfully reloaded configuration files"); else @@ -655,7 +655,7 @@ namespace SharedLibrary.Commands StringBuilder message = new StringBuilder(); var playerAliases = E.Owner.GetAliases(E.Target); - + message.Append("Aliases: "); var names = new List(); @@ -705,5 +705,16 @@ namespace SharedLibrary.Commands } } } + + public class CIP : Command + { + public CIP(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { } + + public override async Task ExecuteAsync(Event E) + { + await E.Origin.Tell($"Your external IP is ^5{E.Origin.IP}"); + } + } } + \ No newline at end of file diff --git a/SharedLibrary/Database.cs b/SharedLibrary/Database.cs index 7d29d065..40385f49 100644 --- a/SharedLibrary/Database.cs +++ b/SharedLibrary/Database.cs @@ -485,11 +485,11 @@ namespace SharedLibrary public List GetAdmins() { List Admins = new List(); - String Query = String.Format("SELECT * FROM CLIENTS WHERE Level >= '{0}'", (int)Player.Permission.Moderator); + String Query = String.Format("SELECT * FROM CLIENTS WHERE Level >= '{0}' ORDER BY Name", (int)Player.Permission.Trusted); DataTable Result = GetDataTable(Query); foreach (DataRow P in Result.Rows) - Admins.Add(new Player(P["Name"].ToString(), P["npID"].ToString(), (Player.Permission)P["Level"], P["IP"].ToString(), P["UID"].ToString())); + Admins.Add(new Player(P["Name"].ToString(), P["npID"].ToString(), (Player.Permission)P["Level"], P["IP"].ToString(), P["UID"].ToString(), Convert.ToInt32(P["Number"].ToString()))); return Admins; } @@ -634,8 +634,8 @@ namespace SharedLibrary { CommandText = "SELECT * FROM ALIASES WHERE NAMES LIKE @name OR IPS LIKE @ip LIMIT 15" }; - cmd.Parameters.AddWithValue("@name", name); - cmd.Parameters.AddWithValue("@ip", DefaultIP); + cmd.Parameters.AddWithValue("@name", '%' + name + '%'); + cmd.Parameters.AddWithValue("@ip", '%' + DefaultIP + '%'); var Result = GetDataTable(cmd); diff --git a/SharedLibrary/Interfaces/IManager.cs b/SharedLibrary/Interfaces/IManager.cs index 27f8ea4d..99913c33 100644 --- a/SharedLibrary/Interfaces/IManager.cs +++ b/SharedLibrary/Interfaces/IManager.cs @@ -15,5 +15,7 @@ namespace SharedLibrary.Interfaces AliasesDB GetAliasesDatabase(); IList GetMessageTokens(); IList GetActiveClients(); + IList GetAliasClients(Player player); + IList GetAliases(Player player); } } diff --git a/SharedLibrary/Player.cs b/SharedLibrary/Player.cs index c9f51837..9a1e55fc 100644 --- a/SharedLibrary/Player.cs +++ b/SharedLibrary/Player.cs @@ -69,7 +69,7 @@ namespace SharedLibrary LastConnection = DateTime.Now; } - public Player(String n, String id, Player.Permission P, String I, String UID) + public Player(String n, String id, Player.Permission P, String I, String UID, int dbid) { Name = n; NetworkID = id; @@ -77,6 +77,7 @@ namespace SharedLibrary IP = I; ClientID = -1; this.UID = UID; + DatabaseID = dbid; } public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2) diff --git a/SharedLibrary/RCON.cs b/SharedLibrary/RCON.cs index 9da08094..b47e1480 100644 --- a/SharedLibrary/RCON.cs +++ b/SharedLibrary/RCON.cs @@ -22,9 +22,6 @@ namespace SharedLibrary.Network static string[] SendQuery(QueryType Type, Server QueryServer, string Parameters = "") { - if (QueryServer.Throttled) - throw new Exceptions.DvarException("Server is RCON throttled"); - var ServerOOBConnection = new UdpClient(); ServerOOBConnection.Client.SendTimeout = 1000; ServerOOBConnection.Client.ReceiveTimeout = 1000; @@ -97,9 +94,6 @@ namespace SharedLibrary.Network public static async Task> GetDvarAsync(this Server server, string dvarName) { - if (server.Throttled) - throw new Exceptions.DvarException("Server is RCON throttled"); - string[] LineSplit = await Task.FromResult(SendQuery(QueryType.DVAR, server, dvarName)); if (LineSplit.Length != 3) @@ -113,7 +107,7 @@ namespace SharedLibrary.Network if (ValueSplit.Length != 5) { - var e = new Exceptions.DvarException("DVAR does not exist"); + var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); e.Data["dvar_name"] = dvarName; throw e; } @@ -127,25 +121,16 @@ namespace SharedLibrary.Network public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue) { - if (server.Throttled) - throw new Exceptions.DvarException("Server is RCON throttled"); - await Task.FromResult(SendQuery(QueryType.DVAR, server, $"set {dvarName} {dvarValue}")); } public static async Task ExecuteCommandAsync(this Server server, string commandName) { - if (server.Throttled) - throw new Exceptions.DvarException("Server is RCON throttled"); - return await Task.FromResult(SendQuery(QueryType.COMMAND, server, commandName).Skip(1).ToArray()); } public static async Task> GetStatusAsync(this Server server) { - if (server.Throttled) - throw new Exceptions.DvarException("Server is RCON throttled"); - string[] response = await Task.FromResult(SendQuery(QueryType.DVAR, server, "status")); return Utilities.PlayersFromStatus(response); } diff --git a/SharedLibrary/Server.cs b/SharedLibrary/Server.cs index 6781629e..b7abea14 100644 --- a/SharedLibrary/Server.cs +++ b/SharedLibrary/Server.cs @@ -63,23 +63,6 @@ namespace SharedLibrary return Players.FindAll(x => x != null); } - - /// - /// Get any know aliases ( name or ip based ) from the database - /// - /// Player to scan for aliases - abstract public List GetAliases(Player Origin); - - public List GetPlayerAliases(Player Origin) - { - List databaseIDs = new List(); - - foreach (Aliases A in GetAliases(Origin)) - databaseIDs.Add(A.Number); - - return Manager.GetClientDatabase().GetPlayers(databaseIDs); - } - /// /// Add a player to the server's player list /// @@ -134,6 +117,16 @@ namespace SharedLibrary { return null; } + + /// + /// Legacy method for the alias command + /// + /// + /// + public IList GetAliases(Player P) + { + return Manager.GetAliases(P); + } /// /// Process any server event