diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index de663943..8c7eef08 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -953,39 +953,6 @@ namespace TShockAPI ThreadPool.QueueUserWorkItem(UpdateManager.CheckUpdate); } - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - private static void UpdateNow(CommandArgs args) - { - Process TServer = Process.GetCurrentProcess(); - - using (var sw = new StreamWriter("pid")) - { - sw.Write(TServer.Id); - } - - using (var sw = new StreamWriter("pn")) - { - sw.Write(TServer.ProcessName + " " + Environment.CommandLine); - } - - using (var client = new WebClient()) - { - client.Headers.Add("user-agent", "TShock"); - byte[] updatefile = client.DownloadData("http://tsupdate.shankshock.com/UpdateTShock.exe"); - - using (var bw = new BinaryWriter(new FileStream("UpdateTShock.exe", FileMode.Create))) - { - bw.Write(updatefile); - } - } - - Process.Start(new ProcessStartInfo("UpdateTShock.exe")); - - TShock.Utils.ForceKickAll("Server shutting down for update!"); - WorldGen.saveWorld(); - Netplay.disconnect = true; - } - #endregion Server Maintenence Commands #region Cause Events and Spawn Monsters Commands @@ -1279,15 +1246,19 @@ namespace TShockAPI } } - private static void StartHardMode(CommandArgs args) - { - WorldGen.StartHardmode(); - } + private static void StartHardMode(CommandArgs args) + { + if (!TShock.Config.DisableHardmode) + WorldGen.StartHardmode(); + else + args.Player.SendMessage("Hardmode is disabled via config", Color.Red); + } - private static void DisableHardMode(CommandArgs args) - { - Main.hardMode = false; - } + private static void DisableHardMode(CommandArgs args) + { + Main.hardMode = false; + args.Player.SendMessage("Hardmode is now disabled", Color.Green); + } private static void ConvertCorruption(CommandArgs args) { diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index 67af949a..c708f7b3 100644 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -83,7 +83,7 @@ namespace TShockAPI [Description("Not implemented")] public string RconPassword = ""; [Description("Not implemented")] public int RconPort = 7777; - [Description("Not implemented")] public string ServerName = ""; + [Description("Used when replying to a rest /status request.")] public string ServerName = ""; [Description("Not implemented")] public string MasterServer = "127.0.0.1"; [Description("Valid types are \"sqlite\" and \"mysql\"")] public string StorageType = "sqlite"; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 783ad398..6224c362 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -932,7 +932,7 @@ namespace TShockAPI Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user: " + args.Player.Name); return true; } - TShock.Utils.ForceKick(args.Player, "Incorrect User Account Password"); + TShock.Utils.ForceKick(args.Player, "Invalid user account password.", true); return true; } if (!string.IsNullOrEmpty(TShock.Config.ServerPassword)) diff --git a/TShockAPI/PacketBufferer.cs b/TShockAPI/PacketBufferer.cs index 4bf7c7eb..28325e0c 100644 --- a/TShockAPI/PacketBufferer.cs +++ b/TShockAPI/PacketBufferer.cs @@ -40,8 +40,8 @@ namespace TShockAPI private int[] Compressed = new int[52]; #if DEBUG_NET - Command dump; - Command flush; + Command dump; + Command flush; #endif public PacketBufferer() @@ -51,10 +51,10 @@ namespace TShockAPI buffers[i] = new PacketBuffer(); #if DEBUG_NET - dump = new Command("superadmin", Dump, "netdump"); - flush = new Command("superadmin", Flush, "netflush"); - Commands.ChatCommands.Add(dump); - Commands.ChatCommands.Add(flush); + dump = new Command("superadmin", Dump, "netdump"); + flush = new Command("superadmin", Flush, "netflush"); + Commands.ChatCommands.Add(dump); + Commands.ChatCommands.Add(flush); #endif NetHooks.SendBytes += ServerHooks_SendBytes; @@ -78,8 +78,8 @@ namespace TShockAPI if (disposing) { #if DEBUG_NET - Commands.ChatCommands.Remove(dump); - Commands.ChatCommands.Remove(flush); + Commands.ChatCommands.Remove(dump); + Commands.ChatCommands.Remove(flush); #endif NetHooks.SendBytes -= ServerHooks_SendBytes; ServerHooks.SocketReset -= ServerHooks_SocketReset; @@ -94,7 +94,7 @@ namespace TShockAPI for (int i = 1; i < Bytes.Length; i++) { sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat(Enum.GetName(typeof (PacketTypes), i) + ":", Packets[i], Bytes[i], - Compressed[i])); + Compressed[i])); } File.WriteAllText(Path.Combine(TShock.SavePath, "dmp.txt"), sb.ToString()); } @@ -167,12 +167,12 @@ namespace TShockAPI lock (buffers[socket.whoAmI]) { #if DEBUG_NET - int size = (count - offset); - var pt = buffer[offset + 4]; + int size = (count - offset); + var pt = buffer[offset + 4]; - Packets[pt]++; - Bytes[pt] += size; - Compressed[pt] += Compress(buffer, offset, count); + Packets[pt]++; + Bytes[pt] += size; + Compressed[pt] += Compress(buffer, offset, count); #endif using (var ms = new MemoryStream(buffer, offset, count)) { @@ -213,17 +213,17 @@ namespace TShockAPI } #if DEBUG_NET - static int Compress(byte[] buffer, int offset, int count) - { - using (var ms = new MemoryStream()) - { - using (var gzip = new GZipStream(ms, CompressionMode.Compress, true)) - { - gzip.Write(buffer, offset, count); - } - return (int)ms.Length; - } - } + static int Compress(byte[] buffer, int offset, int count) + { + using (var ms = new MemoryStream()) + { + using (var gzip = new GZipStream(ms, CompressionMode.Compress, true)) + { + gzip.Write(buffer, offset, count); + } + return (int)ms.Length; + } + } #endif } diff --git a/TShockAPI/Properties/AssemblyInfo.cs b/TShockAPI/Properties/AssemblyInfo.cs index 76cbefd2..6ccd4fac 100644 --- a/TShockAPI/Properties/AssemblyInfo.cs +++ b/TShockAPI/Properties/AssemblyInfo.cs @@ -48,5 +48,5 @@ using System.Runtime.InteropServices; // Build Number // MMdd of the build -[assembly: AssemblyVersion("3.4.2.0101")] -[assembly: AssemblyFileVersion("3.4.2.0101")] \ No newline at end of file +[assembly: AssemblyVersion("3.4.2.0102")] +[assembly: AssemblyFileVersion("3.4.2.0102")] \ No newline at end of file diff --git a/TShockAPI/Rest/Rest.cs b/TShockAPI/Rest/Rest.cs index c8229644..719c501d 100644 --- a/TShockAPI/Rest/Rest.cs +++ b/TShockAPI/Rest/Rest.cs @@ -116,8 +116,8 @@ namespace Rests if (obj == null) throw new NullReferenceException("obj"); - //if (OnRestRequestCall(e)) - // return; + if (OnRestRequestCall(e)) + return; var str = JsonConvert.SerializeObject(obj, Formatting.Indented); e.Response.Connection.Type = ConnectionType.Close; @@ -128,31 +128,44 @@ namespace Rests protected virtual object ProcessRequest(object sender, RequestEventArgs e) { - var uri = e.Request.Uri.AbsolutePath; - uri = uri.TrimEnd('/'); - - foreach (var com in commands) + try { - var verbs = new RestVerbs(); - if (com.HasVerbs) - { - var match = Regex.Match(uri, com.UriVerbMatch); - if (!match.Success) - continue; - if ((match.Groups.Count - 1) != com.UriVerbs.Length) - continue; + var uri = e.Request.Uri.AbsolutePath; + uri = uri.TrimEnd('/'); - for (int i = 0; i < com.UriVerbs.Length; i++) - verbs.Add(com.UriVerbs[i], match.Groups[i + 1].Value); - } - else if (com.UriTemplate.ToLower() != uri.ToLower()) + foreach (var com in commands) { - continue; - } + var verbs = new RestVerbs(); + if (com.HasVerbs) + { + var match = Regex.Match(uri, com.UriVerbMatch); + if (!match.Success) + continue; + if ((match.Groups.Count - 1) != com.UriVerbs.Length) + continue; - var obj = ExecuteCommand(com, verbs, e.Request.Parameters); - if (obj != null) - return obj; + for (int i = 0; i < com.UriVerbs.Length; i++) + verbs.Add(com.UriVerbs[i], match.Groups[i + 1].Value); + } + else if (com.UriTemplate.ToLower() != uri.ToLower()) + { + continue; + } + + var obj = ExecuteCommand(com, verbs, e.Request.Parameters); + if (obj != null) + return obj; + } + } + catch (Exception exception) + { + return new Dictionary + { + {"status", "500"}, + {"error", "Internal server error."}, + {"errormsg", exception.Message}, + {"stacktrace", exception.StackTrace}, + }; } return new Dictionary { diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index 6013297b..b6b0a896 100644 --- a/TShockAPI/Rest/RestManager.cs +++ b/TShockAPI/Rest/RestManager.cs @@ -53,10 +53,18 @@ namespace TShockAPI Rest.Register(new RestCommand("/world/read", WorldRead) {RequiresToken = true}); Rest.Register(new RestCommand("/world/meteor", WorldMeteor) {RequiresToken = true}); Rest.Register(new RestCommand("/world/bloodmoon/{bool}", WorldBloodmoon) {RequiresToken = true}); + Rest.Register(new RestCommand("/v2/world/butcher", Butcher) {RequiresToken = true}); Rest.Register(new RestCommand("/v2/players/read", PlayerReadV2) { RequiresToken = true }); Rest.Register(new RestCommand("/v2/players/kick", PlayerKickV2) { RequiresToken = true }); Rest.Register(new RestCommand("/v2/players/ban", PlayerBanV2) { RequiresToken = true }); + Rest.Register(new RestCommand("/v2/players/kill", PlayerKill) {RequiresToken = true}); + Rest.Register(new RestCommand("/v2/players/mute", PlayerMute) {RequiresToken = true}); + Rest.Register(new RestCommand("/v2/players/unmute", PlayerUnMute) {RequiresToken = true}); + + Rest.Register(new RestCommand("/v2/server/broadcast", Broadcast) { RequiresToken = true}); + Rest.Register(new RestCommand("/v2/server/off", Off) {RequiresToken = true}); + Rest.Register(new RestCommand("/v2/server/rawcmd", ServerCommand) {RequiresToken = true}); #region Deprecated Endpoints Rest.Register(new RestCommand("/bans/read/{user}/info", BanInfo) { RequiresToken = true }); @@ -71,9 +79,65 @@ namespace TShockAPI Rest.Register(new RestCommand("/players/{player}/ban", PlayerBan) { RequiresToken = true }); #endregion - //RegisterExamples(); } + #region RestServerMethods + + private object ServerCommand(RestVerbs verbs, IParameterCollection parameters) + { + if (parameters["cmd"] != null && parameters["cmd"].Trim() != "") + { + TSRestPlayer tr = new TSRestPlayer(); + RestObject ro = new RestObject("200"); + Commands.HandleCommand(tr, parameters["cmd"]); + foreach (string s in tr.GetCommandOutput()) + { + ro.Add("response", s); + } + return ro; + } + RestObject fail = new RestObject("400"); + fail["response"] = "Missing or blank cmd parameter."; + return fail; + } + + private object Off(RestVerbs verbs, IParameterCollection parameters) + { + bool confirm; + bool.TryParse(parameters["confirm"], out confirm); + bool nosave; + bool.TryParse(parameters["nosave"], out nosave); + + if (confirm == true) + { + if (!nosave) + WorldGen.saveWorld(); + Netplay.disconnect = true; + RestObject reply = new RestObject("200"); + reply["response"] = "The server is shutting down."; + return reply; + } + RestObject fail = new RestObject("400"); + fail["response"] = "Invalid/missing confirm switch, and/or missing nosave switch."; + return fail; + } + + private object Broadcast(RestVerbs verbs, IParameterCollection parameters) + { + if (parameters["msg"] != null && parameters["msg"].Trim() != "") + { + TShock.Utils.Broadcast(parameters["msg"]); + RestObject reply = new RestObject("200"); + reply["response"] = "The message was broadcasted successfully."; + return reply; + } + RestObject fail = new RestObject("400"); + fail["response"] = "Broadcast failed."; + return fail; + } + + #endregion + #region RestMethods private object TokenTest(RestVerbs verbs, IParameterCollection parameters) @@ -198,6 +262,382 @@ namespace TShockAPI #endregion + #region RestBanMethods + + private object BanCreate(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var ip = parameters["ip"]; + var name = parameters["name"]; + var reason = parameters["reason"]; + + if (ip == null && name == null) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Required parameters were missing from this API endpoint."); + return returnBlock; + } + + if (ip == null) + { + ip = ""; + } + + if (name == null) + { + name = ""; + } + + if (reason == null) + { + reason = ""; + } + + try + { + TShock.Bans.AddBan(ip, name, reason); + } + catch (Exception) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "The specified ban was unable to be created."); + return returnBlock; + } + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Ban created successfully."); + return returnBlock; + } + + private object BanDestroyV2(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + + var type = parameters["type"]; + if (type == null) + { + returnBlock.Add("Error", "Invalid Type"); + return returnBlock; + } + + var ban = new Ban(); + if (type == "ip") ban = TShock.Bans.GetBanByIp(parameters["user"]); + else if (type == "name") ban = TShock.Bans.GetBanByName(parameters["user"]); + else + { + returnBlock.Add("Error", "Invalid Type"); + return returnBlock; + } + + if (ban == null) + { + return new Dictionary {{"status", "400"}, {"error", "The specified ban does not exist."}}; + } + + try + { + TShock.Bans.RemoveBan(ban.IP); + } + catch (Exception) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "The specified ban was unable to be removed."); + return returnBlock; + } + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Ban deleted successfully."); + return returnBlock; + } + + private object BanInfoV2(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + + var type = parameters["type"]; + if (type == null) + { + returnBlock.Add("Error", "Invalid Type"); + return returnBlock; + } + + var ban = new Ban(); + if (type == "ip") ban = TShock.Bans.GetBanByIp(parameters["user"]); + else if (type == "name") ban = TShock.Bans.GetBanByName(parameters["user"]); + else + { + returnBlock.Add("Error", "Invalid Type"); + return returnBlock; + } + + if (ban == null) + { + return new Dictionary { { "status", "400" }, { "error", "The specified ban does not exist." } }; + } + + returnBlock.Add("status", "200"); + returnBlock.Add("name", ban.Name); + returnBlock.Add("ip", ban.IP); + returnBlock.Add("reason", ban.Reason); + return returnBlock; + } + + #endregion + + #region RestWorldMethods + + private object Butcher(RestVerbs verbs, IParameterCollection parameters) + { + bool killFriendly; + if (!bool.TryParse(parameters["killfriendly"], out killFriendly)) + { + RestObject fail = new RestObject("400"); + fail["response"] = "The given value for killfriendly wasn't a boolean value."; + return fail; + } + if (killFriendly) + { + killFriendly = !killFriendly; + } + + int killcount = 0; + for (int i = 0; i < Main.npc.Length; i++) + { + if (Main.npc[i].active && Main.npc[i].type != 0 && !Main.npc[i].townNPC && (!Main.npc[i].friendly || killFriendly)) + { + TSPlayer.Server.StrikeNPC(i, 99999, 90f, 1); + killcount++; + } + } + + RestObject rj = new RestObject("200"); + rj["response"] = killcount + " NPCs have been killed."; + return rj; + } + + private object WorldRead(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + returnBlock.Add("status", "200"); + returnBlock.Add("name", Main.worldName); + returnBlock.Add("size", Main.maxTilesX + "*" + Main.maxTilesY); + returnBlock.Add("time", Main.time); + returnBlock.Add("daytime", Main.dayTime); + returnBlock.Add("bloodmoon", Main.bloodMoon); + returnBlock.Add("invasionsize", Main.invasionSize); + return returnBlock; + } + + private object WorldMeteor(RestVerbs verbs, IParameterCollection parameters) + { + WorldGen.dropMeteor(); + var returnBlock = new Dictionary(); + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Meteor has been spawned."); + return returnBlock; + } + + private object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var bloodmoonVerb = verbs["bool"]; + bool bloodmoon; + if (bloodmoonVerb == null) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "No parameter was passed."); + return returnBlock; + } + if (!bool.TryParse(bloodmoonVerb, out bloodmoon)) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Unable to parse parameter."); + return returnBlock; + } + Main.bloodMoon = bloodmoon; + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Blood Moon has been set to " + bloodmoon); + return returnBlock; + } + + #endregion + + #region RestPlayerMethods + + private object PlayerUnMute(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var playerParam = parameters["player"]; + var found = TShock.Utils.FindPlayer(playerParam); + var reason = parameters["reason"]; + if (found.Count == 0) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " was not found"); + } + else if (found.Count > 1) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + } + else if (found.Count == 1) + { + var player = found[0]; + player.mute = false; + player.SendMessage("You have been remotely unmuted."); + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Player " + player.Name + " was muted."); + } + return returnBlock; + } + + private object PlayerMute(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var playerParam = parameters["player"]; + var found = TShock.Utils.FindPlayer(playerParam); + var reason = parameters["reason"]; + if (found.Count == 0) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " was not found"); + } + else if (found.Count > 1) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + } + else if (found.Count == 1) + { + var player = found[0]; + player.mute = true; + player.SendMessage("You have been remotely muted."); + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Player " + player.Name + " was muted."); + } + return returnBlock; + } + + private object PlayerList(RestVerbs verbs, IParameterCollection parameters) + { + var activeplayers = Main.player.Where(p => p != null && p.active).ToList(); + string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name)); + var ret = new RestObject("200"); + ret["players"] = currentPlayers; + return ret; + } + + private object PlayerReadV2(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var playerParam = parameters["player"]; + var found = TShock.Utils.FindPlayer(playerParam); + if (found.Count == 0) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " was not found"); + } + else if (found.Count > 1) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + } + else if (found.Count == 1) + { + var player = found[0]; + returnBlock.Add("status", "200"); + returnBlock.Add("nickname", player.Name); + returnBlock.Add("username", player.UserAccountName == null ? "" : player.UserAccountName); + returnBlock.Add("ip", player.IP); + returnBlock.Add("group", player.Group.Name); + returnBlock.Add("position", player.TileX + "," + player.TileY); + var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList(); + returnBlock.Add("inventory", string.Join(", ", activeItems.Select(p => (p.name + ":" + p.stack)))); + returnBlock.Add("buffs", string.Join(", ", player.TPlayer.buffType)); + } + return returnBlock; + } + + private object PlayerKickV2(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var playerParam = parameters["player"]; + var found = TShock.Utils.FindPlayer(playerParam); + var reason = parameters["reason"]; + if (found.Count == 0) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " was not found"); + } + else if (found.Count > 1) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + } + else if (found.Count == 1) + { + var player = found[0]; + TShock.Utils.ForceKick(player, reason == null ? "Kicked via web" : reason); + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Player " + player.Name + " was kicked"); + } + return returnBlock; + } + + private object PlayerBanV2(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var playerParam = parameters["player"]; + var found = TShock.Utils.FindPlayer(playerParam); + var reason = parameters["reason"]; + if (found.Count == 0) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " was not found"); + } + else if (found.Count > 1) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + } + else if (found.Count == 1) + { + var player = found[0]; + TShock.Bans.AddBan(player.IP, player.Name, reason == null ? "Banned via web" : reason); + TShock.Utils.ForceKick(player, reason == null ? "Banned via web" : reason); + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Player " + player.Name + " was banned"); + } + return returnBlock; + } + + private object PlayerKill(RestVerbs verbs, IParameterCollection parameters) + { + var returnBlock = new Dictionary(); + var playerParam = parameters["player"]; + var found = TShock.Utils.FindPlayer(playerParam); + var from = verbs["from"]; + if (found.Count == 0) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " was not found"); + } + else if (found.Count > 1) + { + returnBlock.Add("status", "400"); + returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + } + else if (found.Count == 1) + { + var player = found[0]; + player.DamagePlayer(999999); + player.SendMessage(string.Format("{0} just killed you!", from)); + returnBlock.Add("status", "200"); + returnBlock.Add("response", "Player " + player.Name + " was killed."); + } + return returnBlock; + } + + #endregion + #region Deperecated endpoints private object UserUpdate(RestVerbs verbs, IParameterCollection parameters) @@ -439,311 +879,5 @@ namespace TShockAPI return returnBlock; } #endregion - - #region RestBanMethods - - private object BanCreate(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - var ip = parameters["ip"]; - var name = parameters["name"]; - var reason = parameters["reason"]; - - if (ip == null && name == null) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Required parameters were missing from this API endpoint."); - return returnBlock; - } - - if (ip == null) - { - ip = ""; - } - - if (name == null) - { - name = ""; - } - - if (reason == null) - { - reason = ""; - } - - try - { - TShock.Bans.AddBan(ip, name, reason); - } - catch (Exception) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "The specified ban was unable to be created."); - return returnBlock; - } - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Ban created successfully."); - return returnBlock; - } - - private object BanDestroyV2(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - - var type = parameters["type"]; - if (type == null) - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - var ban = new Ban(); - if (type == "ip") ban = TShock.Bans.GetBanByIp(parameters["user"]); - else if (type == "name") ban = TShock.Bans.GetBanByName(parameters["user"]); - else - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - if (ban == null) - { - return new Dictionary {{"status", "400"}, {"error", "The specified ban does not exist."}}; - } - - try - { - TShock.Bans.RemoveBan(ban.IP); - } - catch (Exception) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "The specified ban was unable to be removed."); - return returnBlock; - } - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Ban deleted successfully."); - return returnBlock; - } - - private object BanInfoV2(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - - var type = parameters["type"]; - if (type == null) - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - var ban = new Ban(); - if (type == "ip") ban = TShock.Bans.GetBanByIp(parameters["user"]); - else if (type == "name") ban = TShock.Bans.GetBanByName(parameters["user"]); - else - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - if (ban == null) - { - return new Dictionary { { "status", "400" }, { "error", "The specified ban does not exist." } }; - } - - returnBlock.Add("status", "200"); - returnBlock.Add("name", ban.Name); - returnBlock.Add("ip", ban.IP); - returnBlock.Add("reason", ban.Reason); - return returnBlock; - } - - #endregion - - #region RestWorldMethods - - private object WorldRead(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - returnBlock.Add("status", "200"); - returnBlock.Add("name", Main.worldName); - returnBlock.Add("size", Main.maxTilesX + "*" + Main.maxTilesY); - returnBlock.Add("time", Main.time); - returnBlock.Add("daytime", Main.dayTime); - returnBlock.Add("bloodmoon", Main.bloodMoon); - returnBlock.Add("invasionsize", Main.invasionSize); - return returnBlock; - } - - private object WorldMeteor(RestVerbs verbs, IParameterCollection parameters) - { - WorldGen.dropMeteor(); - var returnBlock = new Dictionary(); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Meteor has been spawned."); - return returnBlock; - } - - private object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - var bloodmoonVerb = verbs["bool"]; - bool bloodmoon; - if (bloodmoonVerb == null) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "No parameter was passed."); - return returnBlock; - } - if (!bool.TryParse(bloodmoonVerb, out bloodmoon)) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Unable to parse parameter."); - return returnBlock; - } - Main.bloodMoon = bloodmoon; - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Blood Moon has been set to " + bloodmoon); - return returnBlock; - } - - #endregion - - #region RestPlayerMethods - - private object PlayerList(RestVerbs verbs, IParameterCollection parameters) - { - var activeplayers = Main.player.Where(p => p != null && p.active).ToList(); - string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name)); - var ret = new RestObject("200"); - ret["players"] = currentPlayers; - return ret; - } - - private object PlayerReadV2(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - returnBlock.Add("status", "200"); - returnBlock.Add("nickname", player.Name); - returnBlock.Add("username", player.UserAccountName == null ? "" : player.UserAccountName); - returnBlock.Add("ip", player.IP); - returnBlock.Add("group", player.Group.Name); - returnBlock.Add("position", player.TileX + "," + player.TileY); - var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList(); - returnBlock.Add("inventory", string.Join(", ", activeItems.Select(p => p.name))); - returnBlock.Add("buffs", string.Join(", ", player.TPlayer.buffType)); - } - return returnBlock; - } - - private object PlayerKickV2(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - var reason = verbs["reason"]; - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - TShock.Utils.ForceKick(player, reason == null ? "Kicked via web" : reason); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was kicked"); - } - return returnBlock; - } - - private object PlayerBanV2(RestVerbs verbs, IParameterCollection parameters) - { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - var reason = verbs["reason"]; - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - TShock.Bans.AddBan(player.IP, player.Name, reason == null ? "Banned via web" : reason); - TShock.Utils.ForceKick(player, reason == null ? "Banned via web" : reason); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was banned"); - } - return returnBlock; - } - - #endregion - - #region RestExampleMethods - - public void RegisterExamples() - { - Rest.Register(new RestCommand("/HelloWorld/name/{username}", UserTest) {RequiresToken = false}); - Rest.Register(new RestCommand("/wizard/{username}", Wizard) {RequiresToken = false}); - } - - //The Wizard example, for demonstrating the response convention: - private object Wizard(RestVerbs verbs, IParameterCollection parameters) - { - var returnBack = new Dictionary(); - returnBack.Add("status", "200"); //Keep this in everything, 200 = ok, etc. Standard http status codes. - returnBack.Add("error", "(If this failed, you would have a different status code and provide the error object.)"); - //And only include this if the status isn't 200 or a failure - returnBack.Add("Verified Wizard", "You're a wizard, " + verbs["username"]); - //Outline any api calls and possible responses in some form of documentation somewhere - return returnBack; - } - - //http://127.0.0.1:8080/HelloWorld/name/{username}?type=status - private object UserTest(RestVerbs verbs, IParameterCollection parameters) - { - var ret = new Dictionary(); - var type = parameters["type"]; - if (type == null) - { - ret.Add("Error", "Invalid Type"); - return ret; - } - if (type == "status") - { - ret.Add("Users", "Info here"); - return ret; - } - return null; - } - - #endregion } } \ No newline at end of file diff --git a/TShockAPI/Rest/SecureRest.cs b/TShockAPI/Rest/SecureRest.cs index 6af31523..bf41e2cd 100644 --- a/TShockAPI/Rest/SecureRest.cs +++ b/TShockAPI/Rest/SecureRest.cs @@ -41,6 +41,7 @@ namespace Rests { Tokens = new Dictionary(); Register(new RestCommand("/token/create/{username}/{password}", NewToken) {RequiresToken = false}); + Register(new RestCommand("/v2/token/create/{password}", NewTokenV2) { RequiresToken = false }); Register(new RestCommand("/token/destroy/{token}", DestroyToken) {RequiresToken = true}); } @@ -60,6 +61,36 @@ namespace Rests {{"status", "200"}, {"response", "Requested token was successfully destroyed."}}; } + private object NewTokenV2(RestVerbs verbs, IParameterCollection parameters) + { + var user = parameters["username"]; + var pass = verbs["password"]; + + RestObject obj = null; + if (Verify != null) + obj = Verify(user, pass); + + if (obj == null) + obj = new RestObject("401") { Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair." }; + + if (obj.Error != null) + return obj; + + string hash; + var rand = new Random(); + var randbytes = new byte[32]; + do + { + rand.NextBytes(randbytes); + hash = randbytes.Aggregate("", (s, b) => s + b.ToString("X2")); + } while (Tokens.ContainsKey(hash)); + + Tokens.Add(hash, user); + + obj["token"] = hash; + return obj; + } + private object NewToken(RestVerbs verbs, IParameterCollection parameters) { var user = verbs["username"]; @@ -88,6 +119,7 @@ namespace Rests Tokens.Add(hash, user); obj["token"] = hash; + obj["deprecated"] = "This method will be removed from TShock in 3.6."; return obj; } diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 7752778f..46bb3c19 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -73,6 +73,7 @@ namespace TShockAPI public bool IgnoreActionsForClearingTrashCan; public PlayerData PlayerData; public bool RequiresPassword; + public bool SilentKickInProgress; public bool RealPlayer { @@ -404,6 +405,36 @@ namespace TShockAPI } } + public class TSRestPlayer : TSServerPlayer + { + internal List CommandReturn = new List(); + + public TSRestPlayer() + { + Group = new SuperAdminGroup(); + } + + public override void SendMessage(string msg) + { + SendMessage(msg, 0, 255, 0); + } + + public override void SendMessage(string msg, Color color) + { + SendMessage(msg, color.R, color.G, color.B); + } + + public override void SendMessage(string msg, byte red, byte green, byte blue) + { + CommandReturn.Add(msg); + } + + public List GetCommandOutput() + { + return CommandReturn; + } + } + public class TSServerPlayer : TSPlayer { public TSServerPlayer() diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 39af00a7..7a7dbb5a 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -651,12 +651,17 @@ namespace TShockAPI private void OnLeave(int ply) { + var tsplr = Players[ply]; Players[ply] = null; if (tsplr != null && tsplr.ReceivedInfo) { - Utils.Broadcast(tsplr.Name + " has left", Color.Yellow); + if (!tsplr.SilentKickInProgress) + { + Utils.Broadcast(tsplr.Name + " left", Color.Yellow); + } + Log.Info(string.Format("{0} left.", tsplr.Name)); if (tsplr.IsLoggedIn) diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index b77f93f4..95338ba3 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -408,6 +408,15 @@ namespace TShockAPI Log.ConsoleInfo(string.Format("{0} was force kicked for : {1}", player.IP, reason)); } + public void ForceKick(TSPlayer player, string reason, bool silent) + { + player.SilentKickInProgress = true; + if (!player.ConnectionAlive) + return; + player.Disconnect(reason); + Log.ConsoleInfo(string.Format("{0} was force kicked for : {1}", player.IP, reason)); + } + /// /// Kicks a player from the server. ///